Java Nio(一) - Unix IO模型
本系列介绍JAVA的NIO(New IO)系列接口。NIO通过几个部件(主要是:selector
、channel
、buffer
),提供和传统IO不同的读写方式。它 同步非阻塞 IO。由NIO提供的接口看,它的设计思路和Unix的五个IO模型的其中一个:多路转接IO是一样的。所以,在学习具体的NIO代码实现之前,我会先介绍这种IO模型。
Unix的五种IO模型
先看看Unix下可用的5种IO模型:
- 阻塞I/O
- 非阻塞I/O
- 即多路转接IO(又译作:I/O复用)
- 信号驱动I/O(SIGIO)
- 异步I/O(Posix.1的aio_系列函数)
对同步,阻塞等概念和这几种IO的区别,本文不再赘述,可以在文末的参考一节找到相关资料。
除了前两种IO模型,Unix对其他三种IO都提供系统调用或系统函数级别的支持。
多路转接IO的使用场景
多路转接IO的适用场景是:需要对多个描述符(文件描述符:Unix对IO地址的抽象,可以是文件,socket等可读写的地址)进行读写。想象我们需要从两个描述符读:
这种场景下,不能在任意描述符上进行阻塞读,因为会导致进程阻塞,使另一个描述符即使有数据也无法处理。
那可以使用多进程 / 线程,让它们分别进行阻塞读吗?当然可以,但会产生新问题:多进程系统中,需要涉及进程间通信问题,如果使用多线程模型,则需要考虑线程间同步问题。
可以使用非阻塞IO读取吗?使用非阻塞的read,分别轮询两个描述符,当任意一个描述符可用时,才处理。这个方法的不足之处是浪费CPU时间。大多数时间实际上是无数据可读的,因此执行read系统调用会浪费时间。
在这种场景下,比较好的技术是使用I/O多路转换(I/O multiplexing)。它的做法是:我们把这两个描述符构造出一张列表,然后调用select
函数,这个函数会 阻塞,直到任意一个描述符准备好进行IO时,它才会返回,并告知用户进程,哪个描述符已经准备好了。
过程
再整理一下它的工作过程:
- 用户进程构造一张感兴趣的描述符列表,调用
select
方法,交给内核。 - select方法阻塞。
- 当这个描述符列表的任意一个可用时,select方法返回,返回这个描述符。
- 用户进程收到描述符,调用
recvfrom
方法把数据从内核拷贝到用户空间。
时序图如下:
参考
- 《Unix环境高级编程》
- 《Unix网络编程》第六章
- http://shensy.iteye.com/blog/1864781
- http://tutorials.jenkov.com/java-nio/index.html
- http://www.cnblogs.com/fanzhidongyzby/p/4098546.html