上篇文章说过了BIO,这章我们就来聊一聊NIO。
NIO的N指的是new(也称新IO)。
由于BIO的很多操作如等待客户端连接,获取客户端IO数据等都是阻塞的。传统的输入流、输出流都是阻塞式的。且都是通过字节的移动来处理的,也就是说,面向流的输入输出系统一次只能处理一个字节,所以通常效率不高。
Jdk1.4开始,java提供了改进的IO,被统称为新IO(NewIO->nio)在java.nio下:
Channel(通道)和Buffer(缓冲)selector(多路复用器)是NIO的重要对象:
Buffer:使用allocate方法可以创建一个指定容量的xxBuffer对象。主要作用就是装入数据,输出数据。
Channel:channel类似于传统的流对象,但与传统流对象有2个主要区别:
- channel可以直接将指定文件的部分或全部映射为Buffer
- 程序不能直接访问channel中的数据,包括读取和写入,channel只能和Buffer交互
最后来介绍重点介绍多路复用器selector:
Select可以同时监控多个SelectableChannel的IO状况,是非阻塞的核心。
SelectableChannel调用register方法向selector进行注册,
通过调用selector实例的select方法可以获取所有需要进行IO处理的channel。
所有的channel默认都是阻塞模式,必须要非阻塞模式才可以利用非阻塞IO操作,可以调用channel的configureBlocking方法设置是否采用阻塞模式。
梳理下上面所说:服务器上的所有的channel都向selector注册(由selector监控各个已注册的channel的IO状态),当任意channel有可用的IO操作时,selector的select方法会返回大于0的整数,这时可以通过selector的selectedKeys方法获取到这些需要进行IO的channel。也就是说只需要不断的调用selector的select方法就可以知道是否有需要处理的IO操作,如果没有,select方法会被阻塞。Select的底层是操作系统的epoll来实现的,基于事件驱动的思想,这个感兴趣的可以去研究下,在这里不做深究。
总结以上过程不难发现:
NIO的通信过程是:所有连接向selector注册,然后不断向selector轮询有无IO事件,有就进行IO操作。NIO的核心就是多路复用神器selector。
还是做一个形象的比喻:我们还是去饭店吃饭,饭店有一个大堂经理,我们想吃啥有啥要求都直接告诉大堂经理,大堂经理会去处理,一旦我们的菜好了,大堂经理就会提醒我们菜好了,可以吃了。在这个期间,其他客人也可以进来饭店,大堂经理也可以服务其他客人。这个大堂经理就是selector,进去吃饭的人就是一个个客户端请求。