3. Java NIO之Selector如何實現(三)

Selector默認是WindowsSelectorImpl的一個實例,
當調用了SelectorImpl的select()方法的時候,調用selector(timeout),之後會調用lockAndDoSelect(),在這個方法中,主要調用了doSelect()方法,參數與傳進來的一致,以WindowsSelectorImpl爲例子,實現的doSelect()方法。

    protected int doSelect(long var1) throws IOException {
        if(this.channelArray == null) {
            throw new ClosedSelectorException();
        } else {
            this.timeout = var1;
            this.processDeregisterQueue();
            if(this.interruptTriggered) {
                this.resetWakeupSocket();
                return 0;
            } else {
                this.adjustThreadsCount();
                this.finishLock.reset();
                this.startLock.startThreads();
 
                try {
                    this.begin();
 
                    try {
                        this.subSelector.poll();
                    } catch (IOException var7) {
                        this.finishLock.setException(var7);
                    }
 
                    if(this.threads.size() > 0) {
                        this.finishLock.waitForHelperThreads();
                    }
                } finally {
                    this.end();
                }
 
                this.finishLock.checkForException();
                this.processDeregisterQueue();
                int var3 = this.updateSelectedKeys();
                this.resetWakeupSocket();
                return var3;
            }
        }
    }

首先調用processD額registerQueue()方法,從而將已經準備註銷註冊的channel進行註銷註冊。

    void processDeregisterQueue() throws IOException {
        Set var1 = this.cancelledKeys();
        synchronized(var1) {
            if(!var1.isEmpty()) {
                Iterator var3 = var1.iterator();
 
                while(var3.hasNext()) {
                    SelectionKeyImpl var4 = (SelectionKeyImpl)var3.next();
 
                    try {
                        this.implDereg(var4);
                    } catch (SocketException var11) {
                        throw new IOException("Error deregistering key", var11);
                    } finally {
                        var3.remove();
                    }
                }
            }
 
        }
    }

遍歷取得所有要註銷註冊的SelectionKey(加到cancelledKeys中的SelectionKey),並且依次調用implDereg()進行註銷綁定。

    protected void implDereg(SelectionKeyImpl var1) throws IOException {
        int var2 = var1.getIndex();
 
        assert var2 >= 0;
 
        Object var3 = this.closeLock;
        synchronized(this.closeLock) {
            if(var2 != this.totalChannels - 1) {
                SelectionKeyImpl var4 = this.channelArray[this.totalChannels - 1];
                this.channelArray[var2] = var4;
                var4.setIndex(var2);
                this.pollWrapper.replaceEntry(this.pollWrapper, this.totalChannels - 1, this.pollWrapper, var2);
            }
 
            var1.setIndex(-1);
        }
 
        this.channelArray[this.totalChannels - 1] = null;
        --this.totalChannels;
        if(this.totalChannels != 1 && this.totalChannels % 1024 == 1) {
            --this.totalChannels;
            --this.threadsCount;
        }
 
        this.fdMap.remove(var1);
        this.keys.remove(var1);
        this.selectedKeys.remove(var1);
        this.deregister(var1);
        SelectableChannel var7 = var1.channel();
        if(!var7.isOpen() && !var7.isRegistered()) {
            ((SelChImpl)var7).kill();
        }
 
    }

這裏如果需要註銷操作的channel已經是selector中最後一個了,那麼直接從數組中移除。
否則,則需要將當前需要移除的channel與數組最後一個channel進行位置交換,保證數組中間位置連續,再移除。
之後操作即就是對註冊操作的反向操作。

完成真正註銷後,調用adjustThreadCount()來調整線程的數量:

private void adjustThreadsCount() {
    int var1;
    if(this.threadsCount > this.threads.size()) {
        for(var1 = this.threads.size(); var1 < this.threadsCount; ++var1) {
            WindowsSelectorImpl.SelectThread var2 = new WindowsSelectorImpl.SelectThread(var1);
            this.threads.add(var2);
            var2.setDaemon(true);
            var2.start();
        }
    } else if(this.threadsCount < this.threads.size()) {
        for(var1 = this.threads.size() - 1; var1 >= this.threadsCount; --var1) {
            ((WindowsSelectorImpl.SelectThread)this.threads.remove(var1)).makeZombie();
        }
    }
 
}

在Selector中,每1024條channel就需要重新打開一個線程加入進行監聽的操作,這裏,重新根據當前應該有的線程數量與此時現存的線程數量進行比較,進行動態調整。
在此之後,調用begin()方法,準備開始正式進行select操作。

    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);
    }

begin()方法判斷這裏interruptor是否爲空,如果爲空,則會在這裏重新生成一個。
這裏的Interruptor保證了當線程阻塞在io操作上,並且被interruptor時,保證selector能夠被喚醒。

在begin()方法執行完畢後,將會調用其subSelector的poll()方法,正式開始select操作。

private int poll() throws IOException {
    return this.poll0(WindowsSelectorImpl.this.pollWrapper.pollArrayAddress, Math.min(WindowsSelectorImpl.this.totalChannels, 1024), this.readFds, this.writeFds, this.exceptFds, WindowsSelectorImpl.this.timeout);
}
 
private int poll(int var1) throws IOException {
    return this.poll0(WindowsSelectorImpl.this.pollWrapper.pollArrayAddress + (long)(this.pollArrayIndex * PollArrayWrapper.SIZE_POLLFD), Math.min(1024, WindowsSelectorImpl.this.totalChannels - (var1 + 1) * 1024), this.readFds, this.writeFds, this.exceptFds, WindowsSelectorImpl.this.timeout);
}
 
private native int poll0(long var1, int var3, int[] var4, int[] var5, int[] var6, long var7);

這裏poll0()是native方法(原生方法的實現),主要爲了監聽pollWrapper中保存的fd是否有數據進出,如果沒有進出,那麼此處在timeout時間裏一直保持阻塞狀態。

數據監聽完成後取得相應數據,在此之後將重新檢驗一邊已經取消註冊的channel,進行註銷。之後調用updateSelectedKeys()方法。

    private int updateSelectedKeys() {
        ++this.updateCount;
        byte var1 = 0;
        int var4 = var1 + this.subSelector.processSelectedKeys(this.updateCount);
 
        WindowsSelectorImpl.SelectThread var3;
        for(Iterator var2 = this.threads.iterator(); var2.hasNext(); 
                    var4 += var3.subSelector.processSelectedKeys(this.updateCount)) {
            var3 = (WindowsSelectorImpl.SelectThread)var2.next();
        }
 
        return var4;
    }

這裏對所有的線程調用processSelectedKeys來處理所有線程在poll過程中取得的結果進行處理,然後返回所以線程中處理的channel數量的總和。
接下來看processSelectedKeys()方法:

private int processSelectedKeys(long var1) {
    byte var3 = 0;
    int var4 = var3 + this.processFDSet(var1, this.readFds, 1, false);
    var4 += this.processFDSet(var1, this.writeFds, 6, false);
    var4 += this.processFDSet(var1, this.exceptFds, 7, true);
    return var4;
}
 
private int processFDSet(long var1, int[] var3, int var4, boolean var5) {
    int var6 = 0;
 
    for(int var7 = 1; var7 <= var3[0]; ++var7) {
        int var8 = var3[var7];
        if(var8 == WindowsSelectorImpl.this.wakeupSourceFd) {
            synchronized(WindowsSelectorImpl.this.interruptLock) {
                WindowsSelectorImpl.this.interruptTriggered = true;
            }
        } else {
            WindowsSelectorImpl.MapEntry var9 = WindowsSelectorImpl.this.fdMap.get(var8);
            if(var9 != null) {
                SelectionKeyImpl var10 = var9.ski;
                if(!var5 || !(var10.channel() instanceof SocketChannelImpl) || !WindowsSelectorImpl.this.discardUrgentData(var8)) {
                    if(WindowsSelectorImpl.this.selectedKeys.contains(var10)) {
                        if(var9.clearedCount != var1) {
                            if(var10.channel.translateAndSetReadyOps(var4, var10) && var9.updateCount != var1) {
                                var9.updateCount = var1;
                                ++var6;
                            }
                        } else if(var10.channel.translateAndUpdateReadyOps(var4, var10) && var9.updateCount != var1) {
                            var9.updateCount = var1;
                            ++var6;
                        }
 
                        var9.clearedCount = var1;
                    } else {
                        if(var9.clearedCount != var1) {
                            var10.channel.translateAndSetReadyOps(var4, var10);
                            if((var10.nioReadyOps() & var10.nioInterestOps()) != 0) {
                                WindowsSelectorImpl.this.selectedKeys.add(var10);
                                var9.updateCount = var1;
                                ++var6;
                            }
                        } else {
                            var10.channel.translateAndUpdateReadyOps(var4, var10);
                            if((var10.nioReadyOps() & var10.nioInterestOps()) != 0) {
                                WindowsSelectorImpl.this.selectedKeys.add(var10);
                                var9.updateCount = var1;
                                ++var6;
                            }
                        }
 
                        var9.clearedCount = var1;
                    }
                }
            }
        }
    }
 
    return var6;
}

這裏主要對之前監聽到的發生io事件需要處理的fd與對應的channel進行操作,根據讀取到的fd取得selector下注冊了的相應的channel,根據監聽到其所發生事件類型(讀、寫、異常),從而更新channel應該的狀態。
在此操作之後,相應的selector的select也相應完成。

發佈了27 篇原創文章 · 獲贊 4 · 訪問量 2991
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章