源码阅读:理解反应器模式在javaNIO中的实现-Selector
今天来看看Selector源码
又是一长串的注解,太棒了。
第一句:
是提供给Selectablechannel的多路解调器(可不可以理解为分发工具?)
A multiplexor of {@link SelectableChannel} objects.
以下是对类上注解的翻译,接下来就不贴原文了:
selector是通过selectorProvider创建的,其必须通过自身的close()方法才能关闭。
通过selectionKey将selectable channel与seletor绑定。
一个selector维护三个selection键的集合:
key set :存储了当前选择器的channel注册信息,是其余两个set的父集合。
selected-key set: 其存储了监听到至少一个兴趣点(interest)的通道信息。
interest:包括OP_READ、OP_WRITE、OP_CONNECT、OP_ACCEPT,selectionKey可以绑定一个至多个interest。
cancelled-key:是被取消的key的集合,但是其绑定的channel还未被注销。
通过register方法会将key添加到key set中。key set本身是无法被修改的。
当其调用了cancel方法,或者其绑定的channel被关闭时,该key会添加到cancelled-key set中。cancel方法会让key在下一次操作(selection operation)中注销其绑定的channel,并将该key移除key set。
(这边的selection operation指的就是上面的读、写、连接、接收四个OP)
selected-key set里的key只能通过remove方法被移除(包括自身的remove方法以及iterator的remove方法)
关于Selection
selection通过3个方法去获得就绪(能够执行io操作)的个数。
select():
同步获取,如果获取不到,就会一直阻塞。
select(long)
同步获取,但是会设置超时时间。
selectNow()
非阻塞的获取。
这三个方法都会包含3个步骤:
1.
把所有已注销的channel所绑定的key从cancelled-key set中移除。
2.
更新获取对channel所绑定的感兴趣事件就绪的channel,针对这类channel,执行以下两步:
2.1:
如果该channel对应的key还不在selected-key set中,添加进去,并更新ready-operation set中channel所就绪的operations,并清除之前所有的就绪信息(正在情况下之前应该也是没有就绪信息的)。
2.2:
如果已经在selected-key中了,则更新ready-operation set中channel所就绪的operations,并保留之前的就绪信息。(ready operations就是已经就绪的操作(上面所述那4种))
3:
如果key set中任何一个key都没有感兴趣事件,那么步骤不会往下走。
如果在执行第二步时有canceled-key set被添加了,则这些被添加的keys会先执行第一步操作。(即判断这些是否已注销,若已注销,移除之)
总结:在获得selectedKey之前,需要先调用select()方法更新selectedKey信息,获得channel更新的就绪信息。
select()方法会更新selectedKey set,也会从cancelled-key中移除已注销的channel。
关于并发
seletors本身是支持并发线程的,但是其key set不支持。
在执行上述(1) 和(3)时需要对cancelled-key set加上sync方法体。
如果在执行selection operations时更新了channel的感兴趣就绪事件,会等这次执行结束才更新。
自己理解:比如在read时发现可以write了,其write的后续操作handler也要在read结束之后。
可以知道上面的select()方法可能会导致堵塞,有以下三种方式中断:
1.调用wakeup方法。
2.调用close方法。
3.调用当前线程的interrupt方法。
close()方法会给阻塞seletor及其3个set。
好了,接下来看下面的方法:
public abstract class Selector implements Closeable {
//初始化
protected Selector() { }
//由SelectorProvider开启
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
/判断是否开启
public abstract boolean isOpen();
//获得创建selecotr的provider,这个类在下一篇文章来读
public abstract SelectorProvider provider();
//返回keyset,下面的几个方法都在上文有所描述,不再写
public abstract Set<SelectionKey> keys();
public abstract Set<SelectionKey> selectedKeys();
public abstract int selectNow() throws IOException;
public abstract int select(long timeout)
throws IOException;
public abstract int select() throws IOException;
public abstract Selector wakeup();
public abstract void close() throws IOException;
}
总结:
selector将channel与选择器进行绑定,并可以通过select()方法轮询channel的感兴趣事件的就绪状态,并获得selectedKey set(其中就是就绪的keys),通过拿到key可以进行后续操作。
下篇文章来要研究key所属类SelectionKey的组成,以及其provider类SelectorProvider。