Мой Java NIO Selector реализован с использованием select() , поэтому он блокируется до тех пор, пока не произойдет одно из следующих действий:
Исходя из этого, я сделал несколько предположений о случае, когда select()
возвращает 0:
ResultSet
selectedKeys()
и может перейти к следующей итерации цикла, где select()
будет вызван сноваОднако я сталкивался с ситуациями, когда select()
возвращал 0, хотя канал был готов. selectedKeys()
возвращает Set
с 1 SelectionKey
, как и ожидалось.
Даже несколько вызовов select()
всегда возвращали 0, пока канал не был обработан и SelectionKey
не был удален. Эта ситуация в основном заканчивается бесконечным циклом, поскольку select()
не блокируется, но всегда мгновенно возвращает 0.
Упрощенный код:
Selector selector = Selector.open();
SocketChannel channel;
for (...) { // for each node
// Create and connect channels...
...
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ, someRelatedObject);
}
int ready;
Set readyKeys;
while (true) {
ready = selector.select();
readyKeys = selector.selectedKeys();
System.out.println("Ready channels: " + ready);
System.out.println("Selected channels: " + readyKeys.size());
if (ready == 0) {
continue;
}
for (SelectionKey key : readyKeys) {
if (key.isValid() && key.isReadable()) {
// Take action...
}
readyKeys.remove(key);
}
}
Почему select()
возвращает 0 хотя есть готовый канал?
Каков предлагаемый способ борьбы с этим?
РЕДАКТИРОВАТЬ:
Изменение этого:
for (SelectionKey key : readyKeys) {
if (key.isValid() && key.isReadable()) {
// Take action...
}
readyKeys.remove(key);
}
на это
for (SelectionKey key : readyKeys) {
readyKeys.remove(key);
if (key.isValid() && key.isReadable()) {
// Take action...
}
}
решило проблему. В некоторых случаях код продолжал
цикл for
перед remove()
вводом ключа.
РЕДАКТИРОВАТЬ 2:
Недавно я узнал, что мой цикл foreachпо выбранному набору ключей неисправен. foreachиспользует итератор набора. Изменение коллекции напрямую (не через методы итератора) во время итерации по ней может привести к «произвольному, недетерминированному» поведению.
Выбранный набор ключей может предоставлять отказоустойчивыйитератор. Fail-fastитераторы обнаруживают такие модификации и выдают исключение ConcurrentModificationExceptionпри следующей итерации. Таким образом, изменение набора в foreachлибо рискует недетерминированным поведением, либо может вызвать исключения — в зависимости от реализации итератора.
Решение: не используйте foreach. Используйте итератор и удалите ключ через iterator.remove().
Iterator iterator;
SelectionKey key;
while (true) {
// ...
iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
key = iterator.next();
iterator.remove();
// ...
}
}