

为了保证操作系统的稳定性和安全性,一个进程的地址空间划分为 用户空间(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 操作。
零拷贝 NIO
零拷贝是提升 IO 操作性能的一个常用手段,指计算机执行 IO 操作时,CPU 不需要将数据从一个存储区域复制到另一个存储区域,从而可以减少上下文切换以及 CPU 的拷贝时间。 也就是说,零拷贝主要解决操作系统在处理 I/O 操作时频繁复制数据的问题。零拷贝的常见实现技术有:
mmap+write、sendfile和sendfile + DMA gather copy。> Java 对零拷贝的支持:
MappedByteBuffer是 NIO 基于内存映射 (mmap)这种零拷⻉⽅式的提供的⼀种实现,底层实际是调用了 Linux 内核的mmap系统调用。它可以将一个文件或者文件的一部分映射到内存中,形成一个虚拟内存文件,这样就可以直接操作内存中的数据,而不需要通过系统调用来读写文件。FileChannel的transferTo()/transferFrom()是 NIO 基于发送文件(sendfile)这种零拷贝方式的提供的一种实现,底层实际是调用了 Linux 内核的sendfile系统调用。它可以直接将文件数据从磁盘发送到网络,而不需要经过用户空间的缓冲区。
异步 AIO
AIO 也就是 NIO 2。Java 7 中引入了 NIO 的**改进版 NIO 2,**它是异步 IO 模型。
异步 IO 是基于事件和回调机制实现的,也就是应用发起 read 操作之后会直接返回,不会堵塞在那里,当内核后台完成数据拷贝,会执行回调函数来通知应用。




> Java 对零拷贝的支持: