NIO - Selector源码分析
1. 背景
SelectableChannel对象的多路复用器。
可以通过调用Selector.open()方法创建Selector对象。Selector.open()方法会利用系统默认的SelectorProvider创建Selector对象。也可以通过自定义SelectorProvider对象的openSelector方法创建Selector。Selector会一直处于打开状态,直到调用它的close方法。
SelectionKey对象表示每一个注册到Selector的SelectorChannel。每个Selector维护3个集合:
- key集合:表示注册到该Selector的Channel,通过Channel的register方法可以往key集合中添加元素,取消的key在selection操作之间从key集合中移除,key集合不能直接修改,通过keys方法返回。
- selected-key集合:在selection操作中,从所有的key集合中识别出已经ready的key对应的Channel。通过执行set的remove可以移除key或者执行迭代器对象的remove。否则key将不能通过其他方式移除。不可以直接增加到selected-key集合中。
- cancelled-key集合:key已经被取消,但是对应的Channel还没有撤销,这个集合不可以直接访问,这个cancelled-key总是key集合的子集。当key被取消,close对应的Channel或执行它的cancel方法,则添加key到cancelled-key集合中。取消key将导致下一次Selection操作时它的通道被撤销,同时将从所有的Selector的key集合中删除。
备注:新创建的Selector对象,这3个集合都是空集合。
Selection
在每次执行Selection操作时,key可能从selected-key集合中增加或删除,也可能从key集合和cancelled-key集合中删除。通过select(),select(long),selectNow()方法执行Selection操作,包含3个步骤:
(1)
2. Selector源码分析
2.1 API
public abstract class Selector implements Closeable { protected Selector() { } // 创建Selector对象 public static Selector open() throws IOException { return SelectorProvider.provider().openSelector(); } // 检测Selector是否打开 public abstract boolean isOpen(); // 返回创建该Selector的Provider public abstract SelectorProvider provider(); // 返回Key集合,key集合不能被直接修改,只有在被cancel和channel被撤销的时候key才被移除。并且不是线程安全的集合。 public abstract Set<SelectionKey> keys(); // 返回selected-key集合,key可以直接移除,但是不可以直接增加。并且不是线程安全的集合。 public abstract Set<SelectionKey> selectedKeys(); // 选择channel有IO事件的key。 // 该方法是非阻塞的selection操作,如果自上次selection操作之后无channel具有IO事件,该方法会立刻返回零。 // 执行该方法会立刻清除之前执行的wakeup影响。 public abstract int selectNow() throws IOException; // 阻塞操作,只有在以下的状态变化时: //(1)至少有一个IO的channel(2)调用selector.wakeup方法(3)当前线程被interrupt(4)timeout时间到(毫秒) public abstract int select(long timeout) throws IOException; // 阻塞操作,返回条件与select(long timeout)类似 public abstract int select() throws IOException; // 唤醒当前select阻塞的操作:如果另一个线程当前阻塞在select或select(long)方法。 // 如果当前没有select阻塞,则下次执行select或select(long)则直接返回,除非selectNow同时执行; //之后select和select(long)方法会正常阻塞; // 如果在select操作之间多次调用wakeup与调用一次效果是一样的 public abstract Selector wakeup(); // 关闭Selector。 // 调用close方法,如果当前阻塞在selection操作,就像调用wakeup方法一样会立刻中断操作 // 与该selector关联的未cancelled的key将失效,它们的channel将撤销,与Selector相关的其他资源将释放。 // 如果Selector已经关闭,执行这个方法将没有影响。 // selector关闭之后,如果执行与selector相关的操作会报ClosedSelectorException public abstract void close() throws IOException; }
2.2 类图
2.3 AbstractSelector
AbstractSelector主要实现了Selector的打开关闭的状态维护,支持异步关闭和中断的begin和end方法,cancelledKeys等。
package java.nio.channels.spi; import java.io.IOException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.util.HashSet; import java.util.Set; import sun.nio.ch.Interruptible; import java.util.concurrent.atomic.AtomicBoolean; // Selector的基本实现。 public abstract class AbstractSelector extends Selector { private AtomicBoolean selectorOpen = new AtomicBoolean(true); // 是否打开 // The provider that created this selector private final SelectorProvider provider; protected AbstractSelector(SelectorProvider provider) { this.provider = provider; } // 三大key集合之一cancelledKeys private final Set<SelectionKey> cancelledKeys = new HashSet<SelectionKey>(); void cancel(SelectionKey k) { // package-private synchronized (cancelledKeys) { cancelledKeys.add(k); } } public final void close() throws IOException { boolean open = selectorOpen.getAndSet(false); if (!open) return; implCloseSelector();// 只有在Selector未关闭的情况下调用,并且只能被调用一次。 } // 关闭Selector // 这个方法被close方法调用去执行Selector的关闭操作,只有在Selector未关闭的情况下调用,并且只能被调用一次。具体参考上面close实现 protected abstract void implCloseSelector() throws IOException; public final boolean isOpen() { return selectorOpen.get(); } public final SelectorProvider provider() { return provider; } protected final Set<SelectionKey> cancelledKeys() { return cancelledKeys; } // 为Selector注册Channel,这个方法被AbstractSelectableChannel.register方法调用 protected abstract SelectionKey register(AbstractSelectableChannel ch, int ops, Object att); protected final void deregister(AbstractSelectionKey key) { ((AbstractSelectableChannel)key.channel()).removeKey(key); } // -- Interruption machinery -- private Interruptible interruptor = null; // 支持异步关闭和中断的begin和end方法 protected final void begin() { if (interruptor == null) { interruptor = new Interruptible() { public void interrupt(Thread ignore) { AbstractSelector.this.wakeup(); }}; } AbstractInterruptibleChannel.blockedOn(interruptor); Thread me = Thread.currentThread(); if (me.isInterrupted()) interruptor.interrupt(me); } protected final void end() { AbstractInterruptibleChannel.blockedOn(null); } }
备注:支持异步关闭和中断的机制,可以参考http://www.cnblogs.com/lujiango/p/8478154.html
2.4 SelectorImpl
package sun.nio.ch; import java.io.IOException; import java.nio.channels.*; import java.nio.channels.spi.*; import java.net.SocketException; import java.util.*; import sun.misc.*; // Selector的基本实现 abstract class SelectorImpl extends AbstractSelector { // 已经准备IO的keys protected Set<SelectionKey> selectedKeys; // 注册到该Selector的所有key protected HashSet<SelectionKey> keys; // Public views of the key sets private Set<SelectionKey> publicKeys; // 不可变 private Set<SelectionKey> publicSelectedKeys; // 可删除,不可增加 protected SelectorImpl(SelectorProvider sp) { super(sp); keys = new HashSet<SelectionKey>(); selectedKeys = new HashSet<SelectionKey>(); if (Util.atBugLevel("1.4")) { publicKeys = keys; publicSelectedKeys = selectedKeys; } else { publicKeys = Collections.unmodifiableSet(keys); publicSelectedKeys = Util.ungrowableSet(selectedKeys); } } public Set<SelectionKey> keys() { if (!isOpen() && !Util.atBugLevel("1.4")) throw new ClosedSelectorException(); return publicKeys; } public Set<SelectionKey> selectedKeys() { if (!isOpen() && !Util.atBugLevel("1.4")) throw new ClosedSelectorException(); return publicSelectedKeys; } // 对于Windows系统,需要WindowsSelectorImpl实现doSelect方法 protected abstract int doSelect(long timeout) throws IOException; private int lockAndDoSelect(long timeout) throws IOException { synchronized (this) { if (!isOpen()) throw new ClosedSelectorException(); synchronized (publicKeys) { synchronized (publicSelectedKeys) { return doSelect(timeout); } } } } // select方法的实现 public int select(long timeout) throws IOException { if (timeout < 0) throw new IllegalArgumentException("Negative timeout"); return lockAndDoSelect((timeout == 0) ? -1 : timeout); } public int select() throws IOException { return select(0); } public int selectNow() throws IOException { return lockAndDoSelect(0); } public void implCloseSelector() throws IOException { wakeup(); synchronized (this) { synchronized (publicKeys) { synchronized (publicSelectedKeys) { implClose(); } } } } // 需要WindowsSelectorImpl实现 protected abstract void implClose() throws IOException; void putEventOps(SelectionKeyImpl sk, int ops) { } protected final SelectionKey register(AbstractSelectableChannel ch, int ops, Object attachment) { if (!(ch instanceof SelChImpl)) throw new IllegalSelectorException(); SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this); k.attach(attachment); synchronized (publicKeys) { implRegister(k); } k.interestOps(ops); return k; } protected abstract void implRegister(SelectionKeyImpl ski); void processDeregisterQueue() throws IOException { // Precondition: Synchronized on this, keys, and selectedKeys Set cks = cancelledKeys(); synchronized (cks) { if (!cks.isEmpty()) { Iterator i = cks.iterator(); while (i.hasNext()) { SelectionKeyImpl ski = (SelectionKeyImpl)i.next(); try { implDereg(ski); } catch (SocketException se) { IOException ioe = new IOException( "Error deregistering key"); ioe.initCause(se); throw ioe; } finally { i.remove(); } } } } } protected abstract void implDereg(SelectionKeyImpl ski) throws IOException; abstract public Selector wakeup(); // 定义唤醒方法 }
2.5 WindowsSelectorImpl和EPollSelectorImpl
具体会分析windows平台的WindowsSelectorImpl,Linux平台的EPollSelectorImpl暂时不做分析。
3. WindowsSelectorImpl