Selector
Selector是Java NIO中能够通过单线程管理多个NIO通道的组件,而在单线程下实现这个操作就必须需要异步才能实现。
在非NIO模式下,即jdk1.4之前的IO阻塞式IO(Blocking IO)下,传统的java socket监听解决方案是为每一个socket连接建立一个线程,并且在read()调用中阻塞,直到有数据读取进来。这事实上是把每个阻塞的线程当作socket监听器,把java虚拟机的线程调度当作通知机制来使用,很明显这是一种曲线救国的实现路线。毕竟线程的上下文切换还需要消耗额外的资源。所以并不是最高效的实现策略。
真正的就绪选择必须由操作系统来做。操作系统的一项最重要的功能就是处理I/O请求并通知各个线程它们的数据已经准备好了。选择器类提供了这种抽象,使得Java代码能够以可移植的方式,请求底层的操作系统提供就绪选择服务。
核心类
Selector
SelectableChannel
SelectionKey
使用方法
创建Selector
向Selector注册通道
当向Selector中注册Channel时,Channel必须是非阻塞的,所以不可以注册FileChannel,因为FileChannel没有实现SelectableChannel接口,不能配置为非阻塞状态模式。
当将通道Channel注册到Selector中时,需要在第二个参数中指定相应观察事件的集合interest集合
,即SelectionKey中的几个代表状态的整数。如果想注册多种状态需要用位或(|
)操作符进行连接。
SelectionKey.OP_CONNECT |
连接就绪 |
SelectionKey.OP_ACCEPT |
接收就绪 |
SelectionKey.OP_READ |
读就绪 |
SelectionKey.OP_WRITE |
写就绪 |
可以在注册完Channel到Selector之后,通过获取到的SelectionKey来获取ready集合key.readyOps();
,即可以得到所观察事件是否就绪的一个位或操作之后的值,此时只需要使用相应的interest值与readyOps值取与(&)操作即可确定此事件是否就绪。或者使用JDK提供的四个API(而实际JDK底层代码也是取与操作进行判断的)如下:
SelectionKey中还可以添加一些附加对象来标识对应注册的是哪个Channel。方法有两种如下
选择通道
当向Selector中注册了通道,就可以调用select来获取有多少通道发生了我们所感兴趣的(interest集合)事件和。该方法及其重载返回的int值表示有多少通道已经就绪。亦即,自上次调用select()方法后有多少通道变成就绪状态。如果调用select()方法,因为有一个通道变成就绪状态,返回了1,若再次调用select()方法,如果另一个通道就绪了,它会再次返回1。如果对第一个就绪的channel没有做任何操作,现在就有两个就绪的通道,但在每次select()方法调用之间,只有一个通道就绪了。
一旦select()方法返回非零,就可以通过selector.selectedKeys()
方法获得所有的以选择即已就绪SelectionKey,通过遍历获取到是SelectionKey的哪个事件就绪。注意每次迭代末尾的keyIterator.remove()调用。Selector不会自己从已选择键集中移除SelectionKey实例。必须在处理完通道时自己移除。下次该通道变成就绪时,Selector会再次将其放入已选择键集中。
唤醒Selector
某个线程调用select()方法后阻塞了,即使没有通道已经就绪,也有办法让其从select()方法返回。只要让其它线程在第一个线程调用select()方法的那个对象上调用Selector.wakeup()方法即可。阻塞在select()方法上的线程会立马返回。
如果有其它线程调用了wakeup()方法,但当前没有线程阻塞在select()方法上,下个调用select()方法的线程会立即“醒来(wake up)”。
关闭Selector
用完Selector后调用其close()方法会关闭该Selector,且使注册到该Selector上的所有SelectionKey实例无效。通道本身并不会关闭。
Example
SelectorServerTest
SelectorClientTest
Server端测试输出
Client端测试输入与输出
参考资料
JAVA-NIO(英文版) - Ron Hitchens
JAVA-NIO(中文版) - Ron Hitchens(著) 裴小星(译)
Java nio tutorial : http://tutorials.jenkov.com/java-nio/index.html
并发编程网:Java NIO系列教程-中文翻译版 : http://ifeve.com/overview/