Если вы всегда отправляете выполнение в пул потоков после возврата из select (), вы можете немедленно отменить ключ, поскольку вы теряете контроль в течение времени выполнения Runnable.
Пример: Если вы выполните следующий выбор () перед отменой предыдущего ключа (поток все еще ожидает выполнения), он будет действителен, в результате чего другой поток будет переносить уже отправленный ключ. Если один из этих потоков отменяет ключ, другой получит 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.
}
Выполнение Firt почти никогда не вернет ключи, но метод select () в конце вашего цикла МОЖЕТ гарантировать, что канал отмененного ключа будет незарегистрированным (не забывайте, что это ваше значение) от селектора.
Однако, если вы просто выполняете задачу в том же потоке, вы слушаете события селектора, обновляя звуки легче и безопаснее.