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的Providerpublic 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相關的操作會報ClosedSelectorExceptionpublic 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 AbstractSelectorextends Selector {private AtomicBoolean selectorOpen = new AtomicBoolean(true); // 是否打開// The provider that created this selectorprivate final SelectorProvider provider;protected AbstractSelector(SelectorProvider provider) {this.provider = provider;}// 三大key集合之一cancelledKeysprivate final Set<SelectionKey> cancelledKeys = new HashSet<SelectionKey>();void cancel(SelectionKey k) { // package-privatesynchronized (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 SelectorImplextends AbstractSelector {// 已經準備IO的keysprotected Set<SelectionKey> selectedKeys;// 注冊到該Selector的所有keyprotected HashSet<SelectionKey> keys;// Public views of the key setsprivate 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 selectedKeysSet 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
?
?
?
轉載于:https://www.cnblogs.com/lujiango/p/8458214.html
總結
以上是生活随笔為你收集整理的NIO - Selector源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [译] 标准化的包布局(Standard
- 下一篇: poj3296--Rinse(三分)