Java高并发教程:高并发IO的底层原理

Java高并发教程:高并发IO的底层原理

IO读写的基础原理

程序进行IO读写依赖于 操作系统底层的IO读写,主要为read、write两大系统调用。在不同的操作系统中,IO读写的系统调用的名称可能不完全一样,但是基本功能是一样的。

首先我们必须要明白的的是,read系统调用,并不是直接从物理设备把数据读取到内存中;write系统调用,也不是直接把数据写入到物理设备。 上层应用无论时调用操作系统的read,还是write都会涉及缓存区。即,调用read,是把数据从内核缓存区复制到进程缓存区,wirte是把数据从进程缓存区复制到内核缓存区。

所以, 程序的IO操作实际上是缓存的复制,并不是实际物理设备的读写,这项底层的读写交换,是由操作系统内核(Kernel)来完成的。

操作系统基础知识:
1.操作系统与外部设备之间:主要通过中断机制来实现。
2.操作系统与上层应用程序:主要通过异常与系统调用两个机制来实现。

内核缓存区与进程缓存区

缓存的目的是减少频繁地与设备之间的物理交换,因为外部设备的直接读写,都需要操作系统中断,而 中断耗时耗力,所以缓存很有必要

  • 在Linux系统中,操作系统内核只有一个 内核缓冲区
  • 每个用户程序(进程),有自己独立的缓存区,叫做 进程缓存区

用户程序的IO读写程序,在大多数情况下,并没有进行实际的IO操作,而是在进程缓冲区和内核缓冲区之间直接进行数据的交换。

系统调用read&write的流程

到这里,还是需要举个例子:

Java高并发教程:高并发IO的底层原理

比如在Java服务器端,完成一次socket请求和响应,完整的流程如下:·

  1. 客户端请求:Linux通过网卡读取客户端的请求数据,将数据读取到内核缓冲区。·
  2. 获取请求数据:Java服务器通过read系统调用,从Linux内核缓冲区读取数据,再送入Java进程缓冲区。·
  3. 服务器端业务处理:Java服务器在自己的用户空间中处理客户端的请求。
  4. 服务器端返回数据:Java服务器完成处理后,构建好的响应数据,将这些数据从用户缓冲区写入内核缓冲区。这里用到的是write系统调用。·
  5. 发送给客户端:Linux内核通过网络IO,将内核缓冲区中的数据写入网卡,网卡通过底层的通信协议,会将数据发送给目标客户端。

四种主要的IO模型

1.同步阻塞IO(Blocking IO)

阻塞IO指的是 需要内核IO操作彻底完成后,才返回到用户空间执行用户的操作。同步IO指的是用户空间的线程是主动发起IO请求的一方,内核空间是被动接收方。

Java高并发教程:高并发IO的底层原理

总之,阻塞IO的特点是:在内核进行IO执行的两个阶段,用户线程都被阻塞了。

阻塞IO的优点是: 应用的程序开发非常简单;在阻塞等待数据期间,用户线程挂起。在阻塞期间,用户线程基本不会占用CPU资源。

阻塞IO的缺点是:一般情况下, 会为每个连接配备一个独立的线程;反过来说,就是一个线程维护一个连接的IO操作。在并发量小的情况下,这样做没有什么问题。但是,当 在高并发的应用场景下,需要大量的线程来维护大量的网络连接,内存、线程切换开销会非常巨大。因此,基本上阻塞IO模型在高并发应用场景下是不可用的。

2.同步非阻塞IO(Non-blocking IO)

非阻塞IO,指的是 用户空间的程序不需要等待内核IO操作彻底完成,可以立即返回用户空间执行用户的操作,即处于非阻塞的状态,与此同时内核会立即返回给用户一个状态值。

简单来说: 阻塞是指用户空间(调用线程)一直在等待,而不能干别的事情非阻塞是指用户空间(调用线程)拿到内核返回的状态值就返回自己的空间,IO操作可以干就干,不可以干,就去干别的事情。

Java高并发教程:高并发IO的底层原理

在NIO模型中,应用程序一旦开始IO系统调用,会出现以下两种情况:

  • 在内核缓冲区中没有数据的情况下,系统调用会立即返回,返回一个调用失败的信息。
  • 在内核缓冲区中有数据的情况下,是阻塞的,直到数据从内核缓冲复制到用户进程缓冲。复制完成后,系统调用返回成功,应用进程开始处理用户空间的缓存数据。

同步非阻塞IO的特点:应用程序的线程需要不断地进行IO系统调用,轮询数据是否已经准备好,如果没有准备好,就继续轮询,直到完成IO系统调用为止。

同步非阻塞IO的优点:每次发起的IO系统调用,在内核等待数据过程中可以立即返回。用户线程不会阻塞,实时性较好。

同步非阻塞IO的缺点:不 断地轮询内核,这将占用大量的CPU时间,效率低下

总体来说, 在高并发应用场景下,同步非阻塞IO也是不可用的。一般Web服务器不使用这种IO模型。这种IO模型一般很少直接使用,而是在其他IO模型中使用非阻塞IO这一特性。在Java的实际开发中,也不会涉及这种IO模型。

注意:这里所说的NIO(同步非阻塞IO)模型,并非Java的NIO(New IO)库。

3.IO多路复用(IO Multiplexing)

IO多路复用可以 解决非阻塞IO模型中存在的轮询等待问题。在IO多路复用模型中,引入一种 新的系统调用(Linux中,为select/epoll),查询IO的就绪状态。通过该调用,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓存区可读/可写),内核就能够将就绪的装填返回给应用程序。随后,应用程序根据就绪的状态,进行相应IO系统调用。

在IO多路复用模型中 通过select/epoll系统调用,单个应用程序的线程,可以不断地轮询成百上千的socket连接,当某个或者某些socket网络连接有IO就绪的状态,就返回对应的可以执行的读写操作

Java高并发教程:高并发IO的底层原理
  • 选择器注册。在这种模式中,首先, 将需要read操作的目标socket网络连接,提前注册到select/epoll选择器中,Java中对应的选择器类是Selector类。然后,才可以开启整个IO多路复用模型的轮询流程。
  • 就绪状态的轮询。 通过选择器的查询方法,查询注册过的所有socket连接的就绪状态。通过查询的系统调用,内核会返回一个就绪的socket列表。当任何一个注册过的socket中的数据准备好了,内核缓冲区有数据(就绪)了,内核就将该socket加入到就绪的列表中。
  • 用户线程获得了就绪状态的列表后,根据其中的socket连接,发起read系统调用,用户线程阻塞。内核开始复制数据,将数据从内核缓冲区复制到用户缓冲区。
  • 复制完成后,内核返回结果,用户线程才会解除阻塞的状态,用户线程读取到了数据,继续执行。

注意:当用户进程调用了 select查询方法,那么整个线程会被阻塞掉。

IO多路复用模型的IO涉及两种系统调用(System Call),另一种是select/epoll(就绪查询),一种是IO操作。 IO多路复用模型建立在操作系统的基础设施之上,即操作系统的内核必须能够提供多路分离的系统调用select/epoll。

IO多路复用模型的优点:与一个线程维护一个连接的阻塞IO模式相比,使用select/epoll的最大优势在于,一个选择器查询线程可以同时处理成千上万个连接(Connection)。 系统不必创建大量的线程,也不必维护这些线程,从而大大减小了系统的开销Java语言的NIO(New IO)技术,使用的就是IO多路复用模型。在Linux系统上,使用的是epoll系统调用。

IO多路复用模型的缺点:本质上,select/epoll系统调用是阻塞式的,属于同步IO。都需要在读写事件就绪后,由系统调用本身负责进行读写,也就是说这个 读写过程是阻塞的。

4.异步IO(Asynchronous IO)

异步IO模型,简称AIO,用户线程通过系统调用, 先向内核注册某个IO操作。内核在整个IO操作完成后,通知用户程序,用户执行后续的业务操作。

异步IO模型的流程,如下:

Java高并发教程:高并发IO的底层原理
  • 当用户线程 发起了read系统调用,立刻就可以开始做其他事情,用户线程不阻塞。
  • 内核开始IO的第一阶段: 准备数据。等到数据准备好了,内核就会 将数据从内核缓存区复制到用户缓存区
  • 内核会给用户线程发送一个信号,或者回调用户线程注册的回调接口, 告诉用户线程read操作完成了。
  • 用户线程读取用户缓存区的数据,完成后续的业务操作。

理论上来说, 异步IO是真正的异步输入输出,它的吞吐量高于IO多路复用模型的吞吐量。但是它依赖操作系统的实现,比如Linux的异步IO模型目前并不完善,在性能上没有优势。所以,大名鼎鼎的 Netty框架,使用的就是IO多路复用模型,而不是异步IO模型。

参考资料

  • 《Netty、Redis、Zookeeper高并发实战》

Original: https://www.cnblogs.com/MrSaver/p/13040794.html
Author: 子烁爱学习
Title: Java高并发教程:高并发IO的底层原理

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/538098/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Spring Boot启动流程源码解析

    定义: BeanFactoryPostProcessor:是一个接口,它允许自定义修改应用程序上下文的beanDefiinition BeanDefinitionRegistryP…

    Java 2023年6月14日
    067
  • java网络编程(UDP详解)

    UDP详解 一,TCP/IP协议栈中,TCP协议和UDP协议的联系和区别? 联系: TCP和UDP是TCP/IP协议栈中传输层的两个协议,它们使用网络层功能把数据包发送到目的地,从…

    Java 2023年6月8日
    080
  • 从Go编程看IO多路复用Epoll

    IO多路复用使得一个线程就可就可以处理多个网络连接,无需要创建多个线程来处理多个socket连接,减少不必要的资源开销,但是Select还是Poll、Epoll模式都有着不同的区别…

    Java 2023年6月16日
    088
  • 3 垃圾收集算法

    1 垃圾收集三件事 2 对象存活判定算法 2.1 引用计数算法 2.2 可达性分析算法 2.2.1 不可达对象的后置处理 2.3 方法区回收判定 5 垃圾收集算法介绍 5.1 分代…

    Java 2023年6月7日
    098
  • java webSocket

    测试网站:http://www.websocket-test.com/前端代码 后端代码–引入包 -发送消息 Original: https://www.cnblogs.com/…

    Java 2023年6月16日
    067
  • VMware虚拟机 NAT模式 配置静态ip

    前言:Ubuntu 16.04 VMware虚拟机 NAT模式 配置静态ip,这个问题困扰我好长时间,桥接的静态ip我会了,然而用NAT 的方式配置集群会更好。(NAT 方式客户机…

    Java 2023年5月30日
    065
  • 国际化异常:No message found under code ‘xxx’ for locale ‘zh_CN’ 解决办法

    导致该异常可能的原因: 没有配置 LocaleChangeInterceptor 拦截器。 基于 session 获取用户语言设定决定语言区域,必须配置拦截器。 Spring MV…

    Java 2023年6月13日
    084
  • Apache Flink系列-⑤应用程序构建块

    应用程序构建块 有状态函数为构建事件驱动的应用程序提供了一个框架。这里,我们将解释有状态函数体系结构的重要方面。 事件入口 有状态函数应用程序直接位于事件驱动的空间中,因此自然要从…

    Java 2023年6月5日
    0100
  • Redis下载安装

    nosql : 不仅仅只有sql 。 随着业务的增长和产品的完善,急速增长的数据给Oracle数据库带来了很大的压力,而随着我们对产品服务质量要求的提高,传统的数据查询方式已无法满…

    Java 2023年6月9日
    081
  • Java微服务分布式架构

    摘自《Java微服务分布式架构企业实战》 1.传统单体应用架构存在的问题一个完整的单体应用程序通常主要由三部分组成:客户端用户界面、模块和数据库,如图1.1所示。传统单体应用的开发…

    Java 2023年5月29日
    092
  • 一个理科直男如何看《鱿鱼游戏》

    前言 我一向不怎么喜欢看棒子片。 但是十一期间却疯狂的追着一部剧:《鱿鱼游戏》。 这片子在全网实在是太火了,火到全球播放量1.11亿次,成为奈飞收视率最高的全球非英语原创剧。 鱿鱼…

    Java 2023年6月8日
    096
  • 数据库事务与脏读幻读

    事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。 …

    Java 2023年6月13日
    097
  • RocketMQ

    应用场景 主要作用解耦、滑峰填谷 异构系统的整合,这个问题比较容易理解,在原阿里SOA ESB比较火的年代,很多异构系统需要进行互联互通。 应用和应用之间的松耦合,这个在阿里巴巴内…

    Java 2023年5月30日
    084
  • MySQL采用B+树作为索引的原因

    MySQL采用B+树作为索引的原因 1、MySQL的索引结构是如何查询的 在MySQL中,存储的数据记录都是持久化到磁盘中的,数据包含索引和记录,当MySQL查询数据时,由于索引也…

    Java 2023年6月8日
    0131
  • 【力扣】872. 叶子相似的树

    请考虑一棵二叉树上所有的叶子,这些叶子的值按从左到右的顺序排列形成一个 叶值序列 。 举个例子,如上图所示,给定一棵叶值序列为 (6, 7, 4, 9, 8) 的树。 如果有两棵二…

    Java 2023年6月8日
    0106
  • Mybatis的缓存

    Mybatis的一级缓存是 默认开启的,你只要搭建一个Mybatis框架,就可以直接使用一级缓存。 一级缓存是 SqlSession级别的,通过SqlSession查询的数据会被缓…

    Java 2023年6月14日
    0103
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球