我有一个子类化的 Thread
,它有一个私有(private)的 Selector
和一个允许其他线程的公共(public) register(SelectableChannel channel, ...)
方法将 channel 注册到选择器。
如回答here , channel 的 register()
在选择器的 select()
/select(long timeout)
期间阻塞,所以我们需要 wakeup()
选择器。
我的线程无限期地选择(除非它被中断)并且它实际上设法在 channel 的 register()
被调用之前进入下一个选择。所以我想我使用一个简单的锁和 synchronized
block 来确保 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(如建议的 here )来存储注册 channel 或更复杂的锁(如 this)会更好吗?反而?这样做的优点/缺点是什么?或者有什么“更好”的方法吗?
最佳答案
只需将选择器等视为非线程安全的,按照 Darron 的建议在同一个线程上执行所有与选择相关的操作。
NIO 选择器的并发模型是bullshit。我必须大声疾呼,因为这对每个试图研究它的人来说都是在浪费时间。最后得出的结论是,算了,不能并发使用。
关于java - NIO 选择器 : How to properly register new channel while selecting,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12822298/