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

Nil*_*esh 14 java multithreading deadlock nio

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

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

private static Selector selector = Selector.open();
Run Code Online (Sandbox Code Playgroud)

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

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

还有另一个名为Request的类,它具有从通道,进程和调用方法中读取数据以注册通道的方法.

selectonKey = Register.register(socketChannel, SelectionKey.OP_READ);
Run Code Online (Sandbox Code Playgroud)

在这一点上,线程被阻塞,没有提供它正在等待的线索.我已经确认选择器已打开.请帮助我了解如何解决此问题.我可以释放任何锁吗?

任何输入将不胜感激.

添加到我描述的内容中.进一步的测试显示,如果从同一个线程调用Register.register方法,它可以注册,但在此之后,如果其他一些线程尝试调用该方法,则线程不会继续前进.

Dar*_*ron 36

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

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

  • 这是*all*NIO实现的基本功能,完全在Javadoc中指定.当你在`select(),`和`register()`尝试其中一个时,有三个嵌套同步.结果不是死锁,而是一个*块,*只持续到并发`select()`调用返回.解决方法是在`register()之前调用`wakeup()`.这会强制`select()`解除阻塞并返回零,这将释放它的三个锁,这允许`register()`声明它需要的那个然后继续 (5认同)
  • 上述评论中提供的解决方案是错误的 - 至少,给出了解释.根据线程的调度,选择线程可能会完成整个循环并在注册线程能够调用register()之前重新输入select(). (2认同)

Kev*_*ron 11

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

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

final ReentrantLock selectorLock = new ReentrantLock();
Run Code Online (Sandbox Code Playgroud)

然后,当您需要使用选择器注册时,执行以下操作:

selectorLock.lock();
try {
    selector.wakeup();
    socketChannel.register(selector, ops);
} finally {
    selectorLock.unlock();
}
Run Code Online (Sandbox Code Playgroud)

最后,在你的循环中你正在调用accept(),这样的事情:

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

selector.select(500);
Run Code Online (Sandbox Code Playgroud)

然后继续其余的逻辑.

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

  • 代码示例+1。根据经验,我赞同这一点。做得很好。 (2认同)
  • 这会给每次注册带来多达 500 毫秒的不必要延迟。请改用 http://stackoverflow.com/a/2179612/448970 中的方法。 (2认同)

Ada*_*ski 0

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

AbstractSelectableChannel包含一个configureBlocking需要register同步的锁。该锁也可以通过该方法访问blockingLock(),因此另一个线程可能持有该锁,导致您的寄存器调用无限期地阻塞(但如果没有堆栈跟踪,则很难判断)。