У меня есть подклассы Thread
с частным Selector
и общедоступным методом register(SelectableChannel channel, ...)
, который позволяет другим потокам регистрировать каналы для селектора.
Как сказано здесь, канал register()
блокируется во время селектора select()
/select(long timeout)
, поэтому нам нужно wakeup()
селектор.
Моя нить выбирается неограниченно (если только она не прерывается), и ей действительно удается попасть в следующий выбор до вызова канала register()
. Поэтому я подумал, что я использую простой замок с блоками synchronized
, чтобы гарантировать, что register()
произойдет первым.
Код: (ненужный код удален для чтения)
public class SelectorThread extends Thread {
...
public void register(SelectableChannel channel, Attachment attachment) throws IOException {
channel.configureBlocking(false);
synchronized (this) { // LOCKING OCCURS HERE
selector.wakeup();
channel.register(selector,
SelectionKey.OP_READ,
attachment);
}
}
@Override
public void run() {
int ready;
Set<SelectionKey> readyKeys;
while (!isInterrupted()) {
synchronized (this) {} // LOCKING OCCURS HERE
try {
ready = selector.select(5000);
} catch (IOException e) {
e.printStackTrace();
continue;
}
if (ready == 0) {
continue;
}
readyKeys = selector.selectedKeys();
for (SelectionKey key : readyKeys) {
readyKeys.remove(key);
if (!key.isValid()) {
continue;
}
if (key.isReadable()) {
...
}
}
}
}
}
Эта простая блокировка позволяет register()
произойти до того, как поток продолжит следующий цикл выбора. Насколько я проверял, это работает как предполагается.
Вопросы: Это "хороший" способ сделать это или есть какие-то серьезные недостатки? Было бы лучше использовать List или Queue (как предложено здесь) для хранения каналов для регистрации или более сложного блокировки типа это вместо этого? Какими будут плюсы и минусы этого? Или есть какие-то "еще лучшие" способы?