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();
    ......
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章