Java io模型----BIO、NIO、AIO、IO多路复用、零拷贝

java常见io模型,nio,bio,io多路复用,零拷贝,异步io

为了保证操作系统的稳定性和安全性,一个进程的地址空间划分为 用户空间(User space)内核空间(Kernel space )

像我们平常运行的应用程序都是运行在用户空间,只有内核空间才能进行系统态级别的资源有关的操作,比如文件管理、进程通信、内存管理等等。也就是说,我们想要进行 IO 操作,一定是要依赖内核空间的能力。并且,用户空间的程序不能直接访问内核空间。当想要执行 IO 操作时,由于没有执行这些操作的权限,只能发起系统调用请求操作系统帮忙完成。

因此,用户进程想要执行 IO 操作的话,必须通过 系统调用 来间接访问内核空间

我们在平常开发过程中接触最多的就是 磁盘 IO(读写文件)网络 IO(网络请求和响应)

从应用程序的视角来看的话,我们的应用程序对操作系统的内核发起 IO 调用(系统调用),操作系统负责的内核执行具体的 IO 操作。也就是说,我们的应用程序实际上只是发起了 IO 操作的调用而已,具体 IO 的执行是由操作系统的内核来完成的。

同步/异步、阻塞/非阻塞

同步/异步关注的是任务执行的顺序性 ,阻塞/非阻塞关注的是线程是否被挂起,有无回调。

Java 常见 IO 模型

同步阻塞 BIO

同步阻塞 IO 模型中,应用程序发起 read 调用后,会一直阻塞,直到内核把数据拷贝到用户空间。

同步非阻塞 NIO

jdk4 引入,同步非阻塞 IO 模型中,应用程序会一直发起 read 调用,等待数据从内核空间拷贝到用户空间的这段时间里,线程依然是阻塞的,直到在内核把数据拷贝到用户空间。

但是,这种 IO 模型同样存在问题:应用程序不断进行 I/O 系统调用轮询数据是否已经准备好的过程是十分消耗 CPU 资源的。 >

IO 多路复用

IO 多路复用模型中,线程首先发起 select 调用 ,询问内核数据是否准备就绪,等内核把数据准备好了,用户线程再发起 read 调用 。read 调用的过程(数据从内核空间 -> 用户空间)还是阻塞的 。IO 多路复用减少无效的系统调用,减少了对 CPU 资源的消耗。

Java 中的 NIO ,有一个非常重要的选择器 ( Selector ) 的概念,也可以被称为 多路复用器。通过它,只需要一个线程便可以管理多个客户端连接。当客户端数据到了之后,才会为其服务。

注意! 使用 NIO 并不一定意味着高性能,它的性能优势主要体现在高并发和高延迟的网络环境下。当连接数较少、并发程度较低或者网络传输速度较快时,NIO 的性能并不一定优于传统的 BIO 。

到此我们发现,上述几种都不是异步 IO,他们区别在于发起 read 请求时是否阻塞,但是在从内核空间拷贝数据到用户空间的过程中始终是阻塞的。

Buffer

在传统的 BIO 中,数据的读写是面向流的, 分为字节流和字符流。在 Java 1.4 的 NIO 库中,所有数据都是用缓冲区处理的

Channel

channel 是一个双向通道(双全工),可以同时用于读写,而 inputStream 流和 outputStream 流则是单向的

Selector

Selector(选择器) 它允许一个线程处理多个 Channel。Selector 是基于事件驱动的 I/O 多路复用模型。

主要运作原理是:通过 Selector 注册通道的事件,Selector 会不断地轮询注册在其上的 Channel。当事件发生时,比如:某个 Channel 上面有新的 TCP 连接接入、读和写事件,这个 Channel 就处于就绪状态 ,会被 Selector 轮询出来。Selector 会将相关的 Channel 加入到就绪集合中。通过 SelectionKey 可以获取就绪 Channel 的集合,然后对这些就绪的 Channel 进行相应的 I/O 操作。

Java NIO 核心知识总结 | JavaGuide

零拷贝 NIO

零拷贝是提升 IO 操作性能的一个常用手段,指计算机执行 IO 操作时,CPU 不需要将数据从一个存储区域复制到另一个存储区域,从而可以减少上下文切换以及 CPU 的拷贝时间。 也就是说,零拷贝主要解决操作系统在处理 I/O 操作时频繁复制数据的问题。零拷贝的常见实现技术有: mmap+writesendfilesendfile + DMA gather copy > Java 对零拷贝的支持:

  • MappedByteBuffer 是 NIO 基于内存映射mmap)这种零拷⻉⽅式的提供的⼀种实现,底层实际是调用了 Linux 内核的 mmap 系统调用。它可以将一个文件或者文件的一部分映射到内存中,形成一个虚拟内存文件,这样就可以直接操作内存中的数据,而不需要通过系统调用来读写文件。
  • FileChanneltransferTo()/transferFrom()是 NIO 基于发送文件(sendfile)这种零拷贝方式的提供的一种实现,底层实际是调用了 Linux 内核的 sendfile系统调用。它可以直接将文件数据从磁盘发送到网络,而不需要经过用户空间的缓冲区

异步 AIO

AIO 也就是 NIO 2。Java 7 中引入了 NIO 的**改进版 NIO 2,**它是异步 IO 模型。

异步 IO 是基于事件和回调机制实现的,也就是应用发起 read 操作之后会直接返回,不会堵塞在那里,当内核后台完成数据拷贝,会执行回调函数来通知应用。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计