调用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
包含一个锁, configureBlocking
和register
需要在该锁上进行同步。 这个锁也可以通过blockingLock()
方法访问,因此另一个线程可能会持有锁,导致你的寄存器调用无限期地阻塞(但是没有堆栈跟踪很难说)。
从任何线程注册您的频道:
synchronized (selectorLock2) { selector.wakeup(); synchronized (selectorLock1) { channel.register(selector, ops); } }
您的选择器循环应如下所示:
while (true) { synchronized (selectorLock1) { selector.select(); } synchronized (selectorLock2) {} .... }