Почему я получаю CancelledKeyException при прохождении ключей?
Почему я получаю 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);
}
}
Исключение:
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$1.run(AbstractWorker.java:20) [server-21.05.11.jar:na]
at java.lang.Thread.run(Thread.java:619) [na:1.6.0_12]
Ответы
Ответ 1
Один из обработчиков может закрыть канал. Например, обработчик чтения должен закрыть канал, если он читает -1. Таким образом, обработчик записи будет терпеть неудачу. Действительно, isWritable()
завершится неудачно, как я теперь вижу из вашей трассировки стека. Поэтому вы должны протестировать isValid()
с любым другим условием, например. isValid() && isReadable(),
isValid() && isWritable(),
и т.д.
Ответ 2
Решение EJP, похоже, не идеальное... Я думаю, что правильный способ обработки ключа выбора, который был отменен в середине цикла селектора, заключается в том, чтобы окружить его с помощью try/catch и проверить CancelledKeyException
, как в:
try {
if (key.isReadable())
processMessage(key);
if (key.isAcceptable())
acceptConnection(key);
}
catch (CancelledKeyException e) {
logger.debug("Key cancelled... Closing channel.");
key.channel().close();
}
Ответ 3
В моем случае поток, выполняющий отмену, отличается от того, который регистрирует канал для выбора. Кажется, что это исключение происходит, когда ключ отменяется первым, а затем другой поток регистрирует ключ снова без выбора между ними. Поэтому я решил это, просто сделав фиктивный выбор непосредственно перед регистрацией канала снова для выбора. Кажется, что некоторые бухгалтерия выполняется в выборе, который необходим для успешного действия регистрации.
Оба потока используют синхронизированные блоки (всего 3) на общем статическом объекте для блокировки регистрации, отмены и выбора действия соответственно.