If you always dispatch execution to a thread pool after select() return, you may wish to immediately cancel the key, since you loose the control over the time the Runnable will execute.
Ex.: If you perform the next select() before canceling the previous key (the thread is still waiting for execution), it will be valid yet, causing another thread to carry the already dispatched key. If one of these threads cancels the key, the other will get a CancelledKeyException besides introducing unexpected behavior.
Even if you cancel the key, a thread may register the same channel (update selection keys) before the channel become unregistered (due to your previous key.cancel()
). What, again,
will cause a CancelledKeyException.
To get rid of this trap, you may want to handle events always in the next loop:
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 execution will, almost, never return keys, but the select() at the end of your loop MAY guarantee the channel of canceled key be unregistered (bear it's up to your impl) from selector.
However, if you are simply executing a task in the same thread you listen to your selector events, updating sounds easy and safer.