NIO - Selector源码分析
1. 背景
SelectableChannel對(duì)象的多路復(fù)用器。
可以通過(guò)調(diào)用Selector.open()方法創(chuàng)建Selector對(duì)象。Selector.open()方法會(huì)利用系統(tǒng)默認(rèn)的SelectorProvider創(chuàng)建Selector對(duì)象。也可以通過(guò)自定義SelectorProvider對(duì)象的openSelector方法創(chuàng)建Selector。Selector會(huì)一直處于打開(kāi)狀態(tài),直到調(diào)用它的close方法。
SelectionKey對(duì)象表示每一個(gè)注冊(cè)到Selector的SelectorChannel。每個(gè)Selector維護(hù)3個(gè)集合:
- key集合:表示注冊(cè)到該Selector的Channel,通過(guò)Channel的register方法可以往key集合中添加元素,取消的key在selection操作之間從key集合中移除,key集合不能直接修改,通過(guò)keys方法返回。
- selected-key集合:在selection操作中,從所有的key集合中識(shí)別出已經(jīng)ready的key對(duì)應(yīng)的Channel。通過(guò)執(zhí)行set的remove可以移除key或者執(zhí)行迭代器對(duì)象的remove。否則key將不能通過(guò)其他方式移除。不可以直接增加到selected-key集合中。
- cancelled-key集合:key已經(jīng)被取消,但是對(duì)應(yīng)的Channel還沒(méi)有撤銷(xiāo),這個(gè)集合不可以直接訪問(wèn),這個(gè)cancelled-key總是key集合的子集。當(dāng)key被取消,close對(duì)應(yīng)的Channel或執(zhí)行它的cancel方法,則添加key到cancelled-key集合中。取消key將導(dǎo)致下一次Selection操作時(shí)它的通道被撤銷(xiāo),同時(shí)將從所有的Selector的key集合中刪除。
備注:新創(chuàng)建的Selector對(duì)象,這3個(gè)集合都是空集合。
Selection
在每次執(zhí)行Selection操作時(shí),key可能從selected-key集合中增加或刪除,也可能從key集合和cancelled-key集合中刪除。通過(guò)select(),select(long),selectNow()方法執(zhí)行Selection操作,包含3個(gè)步驟:
(1)
?
2. Selector源碼分析
2.1 API
public abstract class Selector implements Closeable {protected Selector() { }// 創(chuàng)建Selector對(duì)象public static Selector open() throws IOException {return SelectorProvider.provider().openSelector();}// 檢測(cè)Selector是否打開(kāi)public abstract boolean isOpen();// 返回創(chuàng)建該Selector的Providerpublic abstract SelectorProvider provider();// 返回Key集合,key集合不能被直接修改,只有在被cancel和channel被撤銷(xiāo)的時(shí)候key才被移除。并且不是線程安全的集合。public abstract Set<SelectionKey> keys();// 返回selected-key集合,key可以直接移除,但是不可以直接增加。并且不是線程安全的集合。public abstract Set<SelectionKey> selectedKeys();// 選擇channel有IO事件的key。// 該方法是非阻塞的selection操作,如果自上次selection操作之后無(wú)channel具有IO事件,該方法會(huì)立刻返回零。// 執(zhí)行該方法會(huì)立刻清除之前執(zhí)行的wakeup影響。public abstract int selectNow() throws IOException;// 阻塞操作,只有在以下的狀態(tài)變化時(shí)://(1)至少有一個(gè)IO的channel(2)調(diào)用selector.wakeup方法(3)當(dāng)前線程被interrupt(4)timeout時(shí)間到(毫秒)public abstract int select(long timeout)throws IOException;// 阻塞操作,返回條件與select(long timeout)類(lèi)似public abstract int select() throws IOException;// 喚醒當(dāng)前select阻塞的操作:如果另一個(gè)線程當(dāng)前阻塞在select或select(long)方法。// 如果當(dāng)前沒(méi)有select阻塞,則下次執(zhí)行select或select(long)則直接返回,除非selectNow同時(shí)執(zhí)行;//之后select和select(long)方法會(huì)正常阻塞;// 如果在select操作之間多次調(diào)用wakeup與調(diào)用一次效果是一樣的public abstract Selector wakeup();// 關(guān)閉Selector。// 調(diào)用close方法,如果當(dāng)前阻塞在selection操作,就像調(diào)用wakeup方法一樣會(huì)立刻中斷操作// 與該selector關(guān)聯(lián)的未cancelled的key將失效,它們的channel將撤銷(xiāo),與Selector相關(guān)的其他資源將釋放。// 如果Selector已經(jīng)關(guān)閉,執(zhí)行這個(gè)方法將沒(méi)有影響。// selector關(guān)閉之后,如果執(zhí)行與selector相關(guān)的操作會(huì)報(bào)ClosedSelectorExceptionpublic abstract void close() throws IOException;}2.2 類(lèi)圖
?
2.3 AbstractSelector
AbstractSelector主要實(shí)現(xiàn)了Selector的打開(kāi)關(guān)閉的狀態(tài)維護(hù),支持異步關(guān)閉和中斷的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的基本實(shí)現(xiàn)。 public abstract class AbstractSelectorextends Selector {private AtomicBoolean selectorOpen = new AtomicBoolean(true); // 是否打開(kāi)// 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未關(guān)閉的情況下調(diào)用,并且只能被調(diào)用一次。}// 關(guān)閉Selector// 這個(gè)方法被close方法調(diào)用去執(zhí)行Selector的關(guān)閉操作,只有在Selector未關(guān)閉的情況下調(diào)用,并且只能被調(diào)用一次。具體參考上面close實(shí)現(xiàn)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注冊(cè)Channel,這個(gè)方法被AbstractSelectableChannel.register方法調(diào)用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;// 支持異步關(guān)閉和中斷的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);}}備注:支持異步關(guān)閉和中斷的機(jī)制,可以參考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的基本實(shí)現(xiàn) abstract class SelectorImplextends AbstractSelector {// 已經(jīng)準(zhǔn)備IO的keysprotected Set<SelectionKey> selectedKeys;// 注冊(cè)到該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;}// 對(duì)于Windows系統(tǒng),需要WindowsSelectorImpl實(shí)現(xiàn)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方法的實(shí)現(xiàn)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實(shí)現(xiàn)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
具體會(huì)分析windows平臺(tái)的WindowsSelectorImpl,Linux平臺(tái)的EPollSelectorImpl暫時(shí)不做分析。
3.?WindowsSelectorImpl
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/lujiango/p/8458214.html
總結(jié)
以上是生活随笔為你收集整理的NIO - Selector源码分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: [译] 标准化的包布局(Standard
- 下一篇: poj3296--Rinse(三分)