Java IO 的方式通常分为阻塞的 BIO(Blocking IO)、同步非阻塞的 NIO(New IO) 和异步非阻塞的 AIO(Asynchronous IO)。
JDK1.4 之前只支持 BIO,JDK1.4 以后开始支持 NIO,JDK1.7 开始支持 AIO。
BIO 是同步阻塞的。服务器的模式为一个连接一个线程。
客户端有连接请求时,就需要启动一个线程进行处理。如果这个连接不做任何事情,就会造成不必要的开销,可以通过线程池机制改善。
NIO 是同步非阻塞的。服务器模式为一个请求一个线程。
NIO 最重要的地方是当一个连接建立后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程即可,当这个线程中的多路复用器进行轮询时,发现连接上有请求的话,才开启一个线程进行处理。
AIO 是异步阻塞的。服务器模式为一个有效请求一个线程。
客户端的 IO 请求都是由操作系统完成,再通知服务器去启动线程进行处理。真正的 IO 读写已经由内核完成了。
是否阻塞
BIO 是阻塞的,NIO 是非阻塞的。
BIO 中一个线程调用 read() / write() 时,该线程被阻塞;
NIO 中一个线程从 Channel 中读取数据到 Buffer 中,可以继续做别的操作,当数据读取到 Buffer 中后,线程再继续处理数据;一个线程请求写入数据到某个 Channel,但不需要等待完全写入,该线程可进行别的操作。
缓冲区(Buffer)
BIO 是面向流的,NIO 是面向缓冲区。
发送给一个通道的所有数据都必须首先放到缓冲区中,同样地,从通道中读取的任何数据都要先读到缓冲区中。也就是说,不会直接对通道进行读写数据,而是要先经过缓冲区。
通道(Channel)
NIO 通过 Chammel 进行读写。
通道和流的不同之处在于:流只能在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类),而通道是双向的,可以用于读、写或者同事用于读写。
通道只能和缓冲区交互,因为有缓冲区,通道可以异步读写。
选择器(Selector)
NIO 中一个线程使用一个选择器 Selector,通过轮询的方式去监听多个 Channel 上的事件。从而让一个线程就可以处理多个事件。
使用 Selector 的好处:使用更少的线程就可以来处理通道了,相比使用多个线程,减少了线程上下文切换带来的开销。