Java 线程块,同时使用 selector 注册通道,同时调用 select()。怎么办?

2022-09-03 03:29:17

我有一个基本问题。为什么以及如何选择通道的寄存器方法可以阻塞调用。让我提供一个场景。

我在类 Register 中创建了一个 Selector 对象,如下所示。

private static Selector selector = Selector.open();

我在同一类(寄存器)中也有一个方法,用于向选择器注册通道。

public static SelectionKey registerChannel(SelectableChannel channel, int ops)
                             throws IOException {
   channel.configureBlocking(false);
   return channel.register(selector, ops);
}

还有另一个名为 Request 的类,它具有从通道读取数据的方法,处理并调用以下方法来注册通道。

selectonKey = Register.register(socketChannel, SelectionKey.OP_READ);

此时,线程被阻塞,无法提供它正在等待什么的线索。我已验证选择器是否打开。请为我提供一些帮助,以了解我该如何解决此问题。有没有我可以释放的锁。

任何意见将不胜感激。

补充我所描述的内容。进一步的测试表明,如果从同一线程调用Register.register方法,它能够注册,但是如果其他线程尝试调用该方法,线程就不会继续前进。


答案 1

这是大多数NIO实现的基本功能,从文档中看不出来。

您需要从执行选择的同一线程进行所有寄存器调用,否则将发生死锁。通常,这是通过提供一个注册/取消注册/兴趣更改队列来完成的,该队列被写入,然后调用selector.wakeup()。当选择线程唤醒时,它会检查队列并执行任何请求的操作。


答案 2

您需要使用锁定并手动同步。

在运行选择器循环的同一线程中,有一个 ReentrantLock:

final ReentrantLock selectorLock = new ReentrantLock();

然后,当您需要向选择器注册时,请执行如下操作:

selectorLock.lock();
try {
    selector.wakeup();
    socketChannel.register(selector, ops);
} finally {
    selectorLock.unlock();
}

最后,在你调用 accept() 的循环中,如下所示:

selectorLock.lock();
selectorLock.unlock();

selector.select(500);

然后继续你的逻辑的其余部分。

此构造通过确保在相应的和调用之间永远不会有另一个调用来保证调用不会阻塞。register()select()wakeup()register()