Selector简述
A multiplexor of {@link SelectableChannel} objects.
参照Java doc中Selector描述的第一句话,Selector的作用是Java NIO中管理一组多路复用的SelectableChannel对象,并能够识别通道是否为诸如读写事件做好准备的组件
Selector的创建过程如下:
// 1.创建Selector
Selector selector = Selector.open();
// 2.将Channel注册到选择器中
// ....... new channel的过程 ....
//Notes:channel要注册到Selector上就必须是非阻塞的,所以FileChannel是不可以使用Selector的,因为FileChannel是阻塞的
channel.configureBlocking(false);
// 第二个参数指定了我们对 Channel 的什么类型的事件感兴趣
SelectionKey key = channel.register(selector , SelectionKey.OP_READ);
// 也可以使用或运算|来组合多个事件,例如
SelectionKey key = channel.register(selector , SelectionKey.OP_READ | SelectionKey.OP_WRITE);
// 不过值得注意的是,一个 Channel 仅仅可以被注册到一个 Selector 一次, 如果将 Channel 注册到 Selector 多次, 那么其实就是相当于更新 SelectionKey 的 interest set.
一个Channel在Selector注册其代表的是一个SelectionKey
事件,SelectionKey
的类型包括:
OP_READ
:可读事件;值为:1<<0
OP_WRITE
:可写事件;值为:1<<2
OP_CONNECT
:客户端连接服务端的事件(tcp连接),一般为创建SocketChannel
客户端channel;值为:1<<3
OP_ACCEPT
:服务端接收客户端连接的事件,一般为创建ServerSocketChannel
服务端channel;值为:1<<4
一个Selector内部维护了三组keys:
key set
:当前channel注册在Selector上所有的key;可调用keys()
获取selected-key set
:当前channel就绪的事件;可调用selectedKeys()
获取cancelled-key
:主动触发SelectionKey#cancel()
方法会放在该集合,前提条件是该channel没有被取消注册;不可通过外部方法调用
Selector类中总共包含以下10个方法:
open()
:创建一个Selector对象isOpen()
:是否是open状态,如果调用了close()
方法则会返回false
provider()
:获取当前Selector的Provider
keys()
:如上文所述,获取当前channel注册在Selector上所有的keyselectedKeys()
:获取当前channel就绪的事件列表selectNow()
:获取当前是否有事件就绪,该方法立即返回结果,不会阻塞;如果返回值>0,则代表存在一个或多个select(long timeout)
:selectNow的阻塞超时方法,超时时间内,有事件就绪时才会返回;否则超过时间也会返回select()
:selectNow的阻塞方法,直到有事件就绪时才会返回wakeup()
:调用该方法会时,阻塞在select()
处的线程会立马返回;(ps:下面一句划重点)即使当前不存在线程阻塞在select()
处,那么下一个执行select()
方法的线程也会立即返回结果,相当于执行了一次selectNow()
方法close()
: 用完Selector
后调用其close()
方法会关闭该Selector,且使注册到该Selector
上的所有SelectionKey
实例无效。channel本身并不会关闭。
关于SelectionKey
谈到Selector就不得不提SelectionKey,两者是紧密关联,配合使用的;如上文所示,往Channel注册Selector会返回一个SelectionKey对象,
这个对象包含了如下内容:
- interest set,当前Channel感兴趣的事件集,即在调用
register
方法设置的interes set - ready set
- channel
- selector
- attached object,可选的附加对象
interest set
可以通过SelectionKey类中的方法来获取和设置interes set
// 返回当前感兴趣的事件列表
int interestSet = key.interestOps();
// 也可通过interestSet判断其中包含的事件
boolean isInterestedInAccept = interestSet & SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;
// 可以通过interestOps(int ops)方法修改事件列表
key.interestOps(interestSet | SelectionKey.OP_WRITE);
ready set
当前Channel就绪的事件列表
int readySet = key.readyOps();
// 也可通过四个方法来分别判断不同事件是否就绪
key.isReadable(); //读事件是否就绪
key.isWritable(); //写事件是否就绪
key.isConnectable(); //客户端连接事件是否就绪
key.isAcceptable(); //服务端连接事件是否就绪
channel和selector
我们可以通过SelectionKey来获取当前的channel和selector
// 返回当前事件关联的通道,可转换的选项包括:`ServerSocketChannel`和`SocketChannel`
Channel channel = key.channel();
//返回当前事件所关联的Selector对象
Selector selector = key.selector()