尽管通道准备就绪,但Java NIO Selector select()返回0

rih*_*iha 9 java nio

我的Java NIO选择器是使用select()实现的,所以它会阻塞,直到出现以下任何一个:

  1. 已注册的频道已准备就绪
  2. 它是唤醒() "编
  3. 线程被打断了

由此,我对select()返回0 的情况做了一些假设:

  • 它一定是理由2.或3.
  • selectedKeys()应该返回一个空的ResultSet
  • 我不需要调用selectedKeys(),可以继续下一个循环迭代,select()再次调用

但是,我遇到了select()返回0的情况,尽管有一个就绪通道.按预期selectedKeys()返回a Set为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);
  }
}
Run Code Online (Sandbox Code Playgroud)

select()尽管有一个现成的频道,为什么返回0?建议的处理方法是什么?

编辑:

改变这个:

  for (SelectionKey key : readyKeys) {
    if (key.isValid() && key.isReadable()) {
      // Take action...
    }

    readyKeys.remove(key);
  }
Run Code Online (Sandbox Code Playgroud)

对此

  for (SelectionKey key : readyKeys) {
    readyKeys.remove(key);

    if (key.isValid() && key.isReadable()) {
      // Take action...
    }
  }
Run Code Online (Sandbox Code Playgroud)

解决了这个问题.在某些情况下,代码将continuefor循环之前remove()荷兰国际集团的关键.

编辑2:

我刚刚了解到我对所选键集的foreach循环不好.foreach使用set的迭代器.在迭代时直接修改集合(而不是通过迭代器的方法)可能会导致"任意的,不确定的"行为.

所选键集可以提供快速失败的迭代器.失败快速迭代器检测此类修改并在下一次迭代时抛出ConcurrentModificationException.因此,在foreach中修改集合可能会导致不确定行为,也可能导致异常 - 具体取决于迭代器实现.

解决方案:不要使用foreach.使用迭代器并通过iterator.remove()删除密钥.

Iterator<SelectionKey> iterator;
SelectionKey key;
while (true) {
  // ...

  iterator = selector.selectedKeys().iterator();
  while (iterator.hasNext()) {
    key = iterator.next();
    iterator.remove();
    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)

ben*_*phy 8

select()返回已更改的键数.因此,如果一个密钥在select()调用之前已经准备就绪,那么它可以返回0但selectedKeys可以是非空的.

  • 可能如果钥匙准备就绪并且没有被移除? (3认同)