Java NIO 选择器唤醒操作

7
请为我提供一个关于两个线程之间使用selector.wakeup();方法的可行示例。
我尝试创建一个简单的程序,在其中一个线程正在等待selector.select()方法。第二个线程创建一些套接字并尝试在selector上注册;此时第一个线程被阻塞。
因此,我需要使用selector的wakeup方法,但是第一个线程似乎没有退出阻塞模式。
wakeup方法的javadoc说明如下:
如果另一个线程当前正在调用Selector.select()或Selector.select(long)方法,则该调用将立即返回。
附注:有一些其他的解决方法;其中之一是select(timeout),但我正在努力找出错误所在。
伪代码如下:
第一个线程:
static Selector selector = Selector.open();
while(true) {
   int n = selectorGlobal.select();
   selectorKeySet = selectorGlobal.selectedKeys().iterator();
   while (selectorKeySet.hasNext()) {
      selectionKey = selectorKeySet.next();
      if (selectionKey.isReadable()) {
         //do something
      }
      if(selectionKey.isAcceptable()) {
         //accept
      }
   }
}

第二个线程:

while (itr.hasNext()) { 
   data = (String) itr.next();
   String IP = data.get(0);
   String Port = data.get(1);

   SocketChannel socketChannel = SocketChannel.open();
   socketChannel.configureBlocking(true);
   boolean isConnected = socketChannel.connect(new InetSocketAddress(IP, Port));
   ClassName.selector.wakeup();
   SelectionKey selectionKey = SelectSockets.registerChannel(ClassName.selector,
                socketChannel, SelectionKey.OP_READ);

}
1个回答

6
如果您在选择器中注册套接字(因为选择器用于非阻塞I/O),则可能不希望从Thread 2中获得阻塞的套接字。我认为让选择器使用SocketChannel.finishConnection()处理连接是常见做法。
此外,您似乎可能会有潜在的竞争条件。想象一下以下事件序列:
1. Thread 1: selector.select() 2. ...时间过去了... 3. Thread 2: Thread1.selector.wakeup() 4. Thread 1: 检查可接受性的键 5. Thread 1: 检查可读性的键 6. Thread 1: 循环 7. Thread 1: selector.select() 8. Thread 2:尝试在选择器中注册(但对于此select()来说已经太晚了)
我建议让Thread 2设置一个SocketChannel,并将其存储在Thread 1可以获取的地方(在执行此操作时,请确保线程安全),然后唤醒选择器,让它在Thread 1中检查现有的键,并在调用Selector.select()之前由Thread 1注册新的SocketChannel。

谢谢Seth。连接到其他套接字是一种非常罕见的活动;因此我希望选择器线程不会每次选择键有活动时都检查某些集合/变量。 - Nilesh
1
在同一线程中执行register()和select(),并让另一个线程调用wakeup()是一个绝妙的想法。谢谢! - Lucio Paiva

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接