Java NIO問題記錄:當channel就緒,select()依然返回0?

問題

今天調試NIO後臺發現一個蛋疼的問題。經過調試之後,出現問題的流程描述如下:

客戶端向服務端發送消息,服務端使用IO多路複用處理輸入,即一個selector監聽多個channel。第一條消息正常接收,發送第二條消息時,select()立即返回0,然後開始continue無限循環,導致第二條消息無法正常處理。

下方是服務端處理select()的代碼(由一個異步線程處理)即read監聽線程

while (!isClosed.get()) {
    if (readSelector.select() == 0) {
        //這裏有一個等待操作,等待註冊結束
        waitSelection(inRegInput);
        continue;
    }
    Iterator<SelectionKey> iterator = readSelector.selectedKeys().iterator();
    while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        if (key.isValid()) {
            // 取消繼續對keyOps的監聽
            key.interestOps(key.readyOps() & ~SelectionKey.OP_READ);
            //線程池執行read操作
            inputHandlePool.execute(new InputHandlerImpl(key));
        }
    }
}

按理說,有新的channel就緒時,select()會返回大於0的數,或者沒有channel就緒,select()也是保持阻塞狀態,不會有返回。可現在問題是,爲什麼它立即返回0了?

原因

在Stack Overflow上找到了答案:

Java NIO Selector select() returns 0 although channels are ready

其實select()是否返回與selectedKeys集合有關。當selectedKeys集合不爲空時,select()會立即返回,但是其返回值是發生改變的keys數量,即新的就緒通道數量,這裏不可能是1。因此我的這個場景下,第一條消息會產生一個新的key,我處理完沒有將其刪除,所以收第二條消息時,認定這個key沒有發生改變,就會導致select()返回0,從而導致無限循環。所以,一定要把key從selectedKeys集合中移除。

解決方法:很簡單,遍歷迭代器的循環中加一句iterator.remove()移除已處理的key。代碼如下:

while (iterator.hasNext()) {
    SelectionKey key = iterator.next();
    iterator.remove();
    ......
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章