我的 Java NIO 选择器是使用实现的select() http://docs.oracle.com/javase/6/docs/api/java/nio/channels/Selector.html#select%28%29所以它会阻塞,直到发生以下任何一种情况:
- 注册频道已准备就绪
- it is wakeup() http://docs.oracle.com/javase/6/docs/api/java/nio/channels/Selector.html#wakeup%28%29'ed
- 线程被中断
由此,我对以下情况做了一些假设:select()
返回 0:
- 肯定是原因2或3。
-
选定的键() http://docs.oracle.com/javase/6/docs/api/java/nio/channels/Selector.html#selectedKeys%28%29应该返回一个空
ResultSet
- 我不需要打电话
selectedKeys()
并且可以继续到下一个循环迭代,其中select()
将会再次被召唤
然而,我遇到过这样的情况select()
尽管有就绪通道,但返回 0。selectedKeys()
返回一个Set
with 1 SelectionKey
正如预期的那样。
甚至多次致电select()
将始终返回 0,直到通道被处理并且SelectionKey
去掉了。这种情况基本上会陷入无限循环select()
不阻塞但总是立即返回 0。
简化代码:
Selector selector = Selector.open();
SocketChannel channel;
for (...) { // for each node
// Create and connect channels...
...
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ, someRelatedObject);
}
int ready;
Set<SelectionKey> readyKeys;
while (true) {
ready = selector.select();
readyKeys = selector.selectedKeys();
System.out.println("Ready channels: " + ready);
System.out.println("Selected channels: " + readyKeys.size());
if (ready == 0) {
continue;
}
for (SelectionKey key : readyKeys) {
if (key.isValid() && key.isReadable()) {
// Take action...
}
readyKeys.remove(key);
}
}
为什么select()
尽管有就绪通道但返回0?
建议的处理方法是什么?
EDIT:
改变这个:
for (SelectionKey key : readyKeys) {
if (key.isValid() && key.isReadable()) {
// Take action...
}
readyKeys.remove(key);
}
to this
for (SelectionKey key : readyKeys) {
readyKeys.remove(key);
if (key.isValid() && key.isReadable()) {
// Take action...
}
}
解决了问题。在某些情况下,代码会continue
the for
循环之前remove()
按下钥匙。
EDIT 2:
我最近才知道我的foreach循环选定的键集很糟糕。foreach使用集合的迭代器。在迭代集合时直接修改集合(而不是通过迭代器的方法)可能会导致“任意的、不确定的”行为。
所选择的键组可以提供快速失败迭代器。快速失败迭代器检测此类修改并抛出并发修改异常 http://docs.oracle.com/javase/6/docs/api/java/util/ConcurrentModificationException.html在下一次迭代时。所以修改集合中foreach要么存在不确定行为的风险,要么可能导致异常 - 取决于迭代器的实现。
解决方案:不要使用foreach。使用迭代器并通过以下方式删除密钥迭代器.remove() http://docs.oracle.com/javase/6/docs/api/java/util/Iterator.html#remove%28%29.
Iterator<SelectionKey> iterator;
SelectionKey key;
while (true) {
// ...
iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
key = iterator.next();
iterator.remove();
// ...
}
}