NIO最佳实践 - SelectableChannel和InterestOps

hot*_*zen 5 java nio selector

使用java.nio,必须通过SelectableChannel注册操作的兴趣:

SelectionKey = SelectableChannel.register(selector, interestInOpsBitmask)
Run Code Online (Sandbox Code Playgroud)

注册兴趣:

  • 通过使用新的Ops执行SelectableChannel.register来覆盖现有的SelectionKey
  • VS. 使用key.interestOps更新现有的SelectionKey(key.interestOps()| newOp)

取消注册兴趣:

  • SelectionKey.cancel和SelectableChannel.register与新的Ops
  • VS. 像上面那样更新现有的SelectionKey

有任何利弊吗?

谢谢

pau*_*art 3

如果您总是在 select() 返回后将执行分派给线程池,您可能希望立即取消该键,因为您失去了对 Runnable 执行时间的控制。

例如:如果在取消前一个键之前执行下一个 select()(线程仍在等待执行),则它仍然有效,导致另一个线程携带已经分派的键。如果这些线程之一取消了该密钥,则另一个线程除了引入意外行为之外还将收到CancelledKeyException异常。

即使您取消该密钥,线程也可能会在通道取消注册之前注册相同的通道(更新选择密钥)(由于您之前的原因key.cancel())。再次,什么会导致CancelledKeyException

要摆脱这个陷阱,您可能希望始终在下一个循环中处理事件:

while (true) { // true or something less risky
    //for each pendingTasks call
    pool.execute(task);
    Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
    while (iter.hasNext()) {
        SelectionKey key = iter.next();
        iter.remove();
        key.cancel();
        //store dispatch for the next while iteration
        pendingTasks.add(task); // do not execute tasks before next select()
    }
    selector.select(TIMEOUT); // or selectNow if there are 
                              //any pending events to handle.
}
Run Code Online (Sandbox Code Playgroud)

第一次执行几乎永远不会返回键,但是循环末尾的 select()可以保证取消键的通道从选择器中注销(由您的实现决定)。

但是,如果您只是在侦听选择器事件的同一线程中执行任务,则更新听起来既简单又安全。