调用select()时,Java节点在使用选择器注册通道时阻塞。 该怎么办?

我有一个基本问题。 SelectableChannel的寄存器方法为何以及如何阻止调用。 让我提供一个场景。

我在类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方法,它可以注册但在此之后如果其他一些线程试图调用该方法,则线程不会继续前进。

这是大多数NIO实现的基本function,这些function在文档中并不明显。

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

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

在运行选择器循环的同一个线程中有一个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); 

然后继续其余的逻辑。

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

您是否尝试在程序中打印所有线程的堆栈跟踪(在Unix中使用kill -QUIT或在Windows中使用Ctrl + Break或使用jstack实用程序)?

AbstractSelectableChannel包含一个锁, configureBlockingregister需要在该锁上进行同步。 这个锁也可以通过blockingLock()方法访问,因此另一个线程可能会持有锁,导致你的寄存器调用无限期地阻塞(但是没有堆栈跟踪很难说)。

从任何线程注册您的频道:

 synchronized (selectorLock2) { selector.wakeup(); synchronized (selectorLock1) { channel.register(selector, ops); } } 

您的选择器循环应如下所示:

 while (true) { synchronized (selectorLock1) { selector.select(); } synchronized (selectorLock2) {} .... }