java 为什么在通过密钥时会收到 CancelledKeyException?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6215065/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 14:52:56  来源:igfitidea点击:

Why do I get CancelledKeyException when going through the keys?

javaexceptionnetworkingnio

提问by Rihards

Why do I get the CancelledKeyExceptionfew times a day? Should I do something about it? Is my code wrong?

为什么我CancelledKeyException一天收到几次?我应该做些什么吗?我的代码有错吗?

        Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
        while (keys.hasNext()) {

            SelectionKey key = (SelectionKey) keys.next();
            keys.remove();

            try {
                if (key.isValid()) {
                    if (key.isReadable()) {
                        readHandler.handle((Connection) key.attachment());
                    }
                    if (key.isWritable()) {
                        writeHandler.handle((Connection) key.attachment());
                    }
                    if (key.isAcceptable()) {
                        acceptHandler.handle(key);
                    }
                }
            } catch (CancelledKeyException e) {
                _logger.error("CanceledKeyException in while loop:", e);
            }
        }

Exception:

例外:

java.nio.channels.CancelledKeyException: null
    at sun.nio.ch.SelectionKeyImpl.ensureValid(SelectionKeyImpl.java:55) ~[na:1.6.0_12]
    at sun.nio.ch.SelectionKeyImpl.readyOps(SelectionKeyImpl.java:69) ~[na:1.6.0_12]
    at java.nio.channels.SelectionKey.isWritable(SelectionKey.java:294) ~[na:1.6.0_12]
    at project.engine.io.SimpleReactor.work(SimpleReactor.java:194) ~[engine-02.06.11.jar:na]
    at project.server.work.AbstractWorker.run(AbstractWorker.java:20) [server-21.05.11.jar:na]
    at java.lang.Thread.run(Thread.java:619) [na:1.6.0_12]

采纳答案by user207421

One of the handlers may close the channel. For example, the read handler shouldclose the channel if it reads a -1. So the write handler will then fail. Indeed isWritable()will fail, as I can now see from your stack trace. So you must test isValid()with every other condition, e.g. isValid() && isReadable(),isValid() && isWritable(),etc.

处理程序之一可能会关闭通道。例如,如果读取处理程序读取到 -1,它应该关闭通道。因此写入处理程序将失败。确实isWritable()会失败,正如我现在可以从您的堆栈跟踪中看到的那样。因此,您必须isValid()在所有其他条件下进行测试,例如isValid() && isReadable(),isValid() && isWritable(),等。

回答by Eduardo Bezerra

EJP's solution doesn't seem to be the ideal one... I think the correct way to handle a selection key that was canceled in the middle of the selector loop is to surround it with a try/catch and check for a CancelledKeyException, as in:

EJP 的解决方案似乎不是理想的解决方案......我认为处理在选择器循环中间取消的选择键的正确方法是用 try/catch 包围它并检查 a CancelledKeyException,如:

try {
   if (key.isReadable())
      processMessage(key);

   if (key.isAcceptable())
      acceptConnection(key);
}
catch (CancelledKeyException e) {
   logger.debug("Key cancelled... Closing channel.");
   key.channel().close();
}

回答by Heymen Nicolaij

In my case the thread doing the cancel is a different one than the one which registers the channel for selection. It seems this exception happens when the key is being cancelled first and next the other thread is registering the key again without a select in between. So I solved it by just doing a dummy select just prior to registering the channel again for select. It seems some bookkeeping is done in the select which is neccessary for the registration action to be succesful. Both threads use synchronized blocks (3 in total) on a shared static object to lock the registration, cancel and select action respectively.

在我的情况下,执行取消的线程与注册通道以供选择的线程不同。当密钥首先被取消,然后另一个线程再次注册密钥而没有选择时,似乎会发生此异常。所以我通过在再次注册频道进行选择之前做一个虚拟选择来解决它。似乎在选择中进行了一些簿记,这是注册操作成功所必需的。两个线程在共享静态对象上使用同步块(共 3 个)分别锁定注册、取消和选择操作。