Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/346.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java非阻塞IO选择器导致通道寄存器阻塞_Java_Sockets_Nonblocking - Fatal编程技术网

Java非阻塞IO选择器导致通道寄存器阻塞

Java非阻塞IO选择器导致通道寄存器阻塞,java,sockets,nonblocking,Java,Sockets,Nonblocking,我有两个线程正在处理JavaNIO的非阻塞套接字。这是线程正在执行的操作: 线程1: 调用选择器的select()方法的循环。如果有任何键可用,则会相应地对其进行处理 线程2: 偶尔通过调用register()将SocketChannel注册到选择器 问题是,除非select()的超时非常小(大约100ms),否则对register()的调用将无限期地阻塞。即使通道被配置为非阻塞,并且javadocs声明选择器对象是线程安全的(但我知道它的选择键不是) 有人对这个问题有什么想法吗?如果我将所有内

我有两个线程正在处理JavaNIO的非阻塞套接字。这是线程正在执行的操作:

线程1: 调用选择器的select()方法的循环。如果有任何键可用,则会相应地对其进行处理

线程2: 偶尔通过调用register()将SocketChannel注册到选择器

问题是,除非select()的超时非常小(大约100ms),否则对register()的调用将无限期地阻塞。即使通道被配置为非阻塞,并且javadocs声明选择器对象是线程安全的(但我知道它的选择键不是)

有人对这个问题有什么想法吗?如果我将所有内容都放在一个线程中,那么应用程序就可以完美地工作。没有问题发生,但我真的希望有单独的线程。感谢您的帮助。我在下面发布了我的示例代码:

将select(1000)更改为select(100),它将工作。将其保留为select()或select(1000),则不会

} } 捕获(IOE异常) { 系统错误println(e); }
}

公共UDPSocket(字符串DSTOST,int dstPort) { 尝试 { this.dstHost=dstHost; this.dstPort=dstPort; clientChannel=DatagramChannel.open(); clientChannel.configureBlocking(false); } 捕获(IOE异常) { 系统错误println(e); } }

public void addListener(SocketSubscriber订户) { 尝试 { DatagramChannel服务器通道=DatagramChannel.open(); serverChannel.configureBlocking(false); DatagramSocket socket=serverChannel.socket(); 绑定(新的InetSocketAddress(dstPort)); SelectionKey=serverChannel.register(recvSelector,SelectionKey.OP_READ); 密钥。附加(订户); } 捕获(IOE异常) { 系统错误println(e); } }

公共无效发送(ByteBuffer缓冲区) { 尝试 { send(缓冲区,新的InetSocketAddress(dstHost,dstPort)); } 捕获(IOE异常) { 系统错误println(e); } }

公众假期结束() { 尝试 { clientChannel.close(); } 捕获(IOE异常) { 系统错误println(e); } } }


导入java.nio.ByteBuffer

公共接口SocketSubscriber { 公共数据无效(ByteBuffer数据); }

用法示例:
Object registeringSync = new Object();

公共类测试实现SocketSubscriber
{
公共静态void main(字符串[]args)引发异常
{
UDPSocket.init();
UDPSocket测试=新的UDPSocket(“本地主机”,1234);
addListener(newtest());
udpsockettest2=新的UDPSocket(“localhost”,4321);
test2.addListener(newtest());
System.out.println(“侦听…”);
ByteBuffer缓冲区=ByteBuffer.allocate(500);
发送(缓冲区);
buffer.rewind();
test2.send(缓冲区);
System.out.println(“数据发送…”);
睡眠(5000);
UDPSocket.shutdown();
}

@凌驾 公共无效数据(ByteBuffer数据) { System.out.println(“接收”+数据.limit()+“数据字节”); } }

选择器具有多个记录在案的内部同步级别,您将遇到所有这些级别。在调用
register()之前,先在选择器上调用
wakeup()
如果选择的键为零,请确保
select()
循环正常工作,这就是
wakeup()上会发生的情况。

我今天遇到了相同的问题(即“wakeupAndRegister”不可用)。我希望我的解决方案可能会有所帮助:

创建同步对象:

synchronized (registeringSync) {
  selector.wakeup();  // Wakes up a CURRENT or (important) NEXT select
  // !!! Might run into a deadlock "between" these lines if not using the lock !!!
  // To force it, insert Thread.sleep(1000); here
  channel.register(selector, ...);
}
通过执行以下操作注册频道:

public void run() {    
  while (initialized) {
    if (selector.select() != 0) {  // Blocks until "wakeup"
      // Iterate through selected keys
    }
    synchronized (registeringSync) { }  // Cannot continue until "register" is complete
  }
}
线程应执行以下操作:


我遇到了与海报相同的问题,但我对这个解决方案并不完全满意。由于线程调度,在寄存器发生之前,没有任何东西阻止唤醒和重新选择。@Davidermann如果您可以控制调用
select()
的线程的代码,则可以实现某种控制以防止立即重新选择,例如
if(select()==0)sleep(1)
可能会产生足够的
唤醒();寄存器()select()
发生之前执行的代码>。解决方案仍然有效(+1),问题来自一个“缺少的方法”
wakeupAndRegister()
,该方法将以原子方式执行。@davidermann我对整个设计不满意。为什么要指定三个级别的同步,并将其硬连接到抽象基类中,这对我来说是一个谜,因为除了这个奇怪的
wakeup()之外,从一个单独线程注册的机制几乎完全留给了我们
method.@davidermann有关您的问题,请参见。如果问题可能导致死锁,那么执行“睡眠(1)”之类的操作是一个非常糟糕的主意。多线程不能做“好”或“坏”。。。不管“对”与否。不管怎样,每次出现“睡眠”都可能是不好的。已经有三个级别的同步,这就是问题所在。添加第四个并不能解决任何问题,它解决了。如果不进行额外的锁定,如果线程进入一个新的select“BEVER”wakeup和register(在那里添加了注释),代码可能会陷入死锁。没有死锁。死锁要求两个线程都被阻塞,等待对方的锁。不要误用标准术语。标准术语可能重复
Object registeringSync = new Object();
synchronized (registeringSync) {
  selector.wakeup();  // Wakes up a CURRENT or (important) NEXT select
  // !!! Might run into a deadlock "between" these lines if not using the lock !!!
  // To force it, insert Thread.sleep(1000); here
  channel.register(selector, ...);
}
public void run() {    
  while (initialized) {
    if (selector.select() != 0) {  // Blocks until "wakeup"
      // Iterate through selected keys
    }
    synchronized (registeringSync) { }  // Cannot continue until "register" is complete
  }
}