2.5 4种I/O的对比

2.5.1 概念澄清

为了防止由于对一些技术概念和术语的理解或者叫法不一致而引起歧义,本小节特意对本书中的专业术语或者技术用语做下声明:如果它们与其他一些技术书籍的称呼不一致,请以本小节的解释为准。

1.异步非阻塞I/O

很多人喜欢将JDK1.4提供的NIO框架称为异步非阻塞I/O,但是,如果严格按照UNIX网络编程模型和JDK的实现进行区分,实际上它只能被称为非阻塞I/O,不能叫异步非阻塞I/O。在早期的JDK1.4和1.5 update10版本之前,JDK的Selector基于select/poll模型实现,它是基于I/O复用技术的非阻塞I/O,不是异步I/O。在JDK1.5 update10和Linux core2.6以上版本,Sun优化了Selctor的实现,它在底层使用epoll替换了select/poll,上层的API并没有变化,可以认为是JDK NIO的一次性能优化,但是它仍旧没有改变I/O的模型。相关优化的官方说明如图2-17所示。

由JDK1.7提供的NIO2.0,新增了异步的套接字通道,它是真正的异步I/O,在异步I/O操作的时候可以传递信号变量,当操作完成之后会回调相关的方法,异步I/O也被称为AIO。

NIO类库支持非阻塞读和写操作,相比于之前的同步阻塞读和写,它是异步的,因此很多人习惯于称NIO为异步非阻塞I/O,包括很多介绍NIO编程的书籍也沿用了这个说法。为了符合大家的习惯,本书也会将NIO称为异步非阻塞I/O或者非阻塞I/O,请大家理解,不要过分纠结在一些技术术语的咬文嚼字上。

图2-17 JDK1.5_update10支持epoll

2.多路复用器Selector

几乎所有的中文技术书籍都将Selector翻译为选择器,但是实际上我认为这样的翻译并不恰当,选择器仅仅是字面上的意思,体现不出Selector的功能和特点。

在前面的章节我们介绍过Java NIO的实现关键是多路复用I/O技术,多路复用的核心就是通过Selector来轮询注册在其上的Channel,当发现某个或者多个Channel处于就绪状态后,从阻塞状态返回就绪的Channel的选择键集合,进行I/O操作。由于多路复用器是NIO实现非阻塞I/O的关键,它又是主要通过Selector实现的,所以本书将Selector翻译为多路复用器,与其他技术书籍所说的选择器是同一个东西,请大家了解。

3.伪异步I/O

伪异步I/O的概念完全来源于实践。在JDK NIO编程没有流行之前,为了解决Tomcat通信线程同步I/O导致业务线程被挂住的问题,大家想到了一个办法:在通信线程和业务线程之间做个缓冲区,这个缓冲区用于隔离I/O线程和业务线程间的直接访问,这样业务线程就不会被I/O线程阻塞。而对于后端的业务侧来说,将消息或者Task放到线程池后就返回了,它不再直接访问I/O线程或者进行I/O读写,这样也就不会被同步阻塞。类似的设计还包括前端启动一组线程,将接收的客户端封装成Task,放到后端的线程池执行,用于解决一连接一线程问题。像这样通过线程池做缓冲区的做法,本书中习惯于称它为伪异步I/O,而官方并没有伪异步I/O这种说法,请大家注意。

下面的小节我们对几种常见的I/O进行对比,以便大家能够理解几种I/O的差异。