Java 客户端线程挂起仿真阻止服务器在客户端设置为等待的时间内接受任何I/O

Java 客户端线程挂起仿真阻止服务器在客户端设置为等待的时间内接受任何I/O,java,multithreading,concurrency,client,Java,Multithreading,Concurrency,Client,正如主题所示,我有一个服务器和一些客户端 服务器同时接受I/O连接(套接字连接中没有排队),但我有这个麻烦的问题,我不知道如何绕过它! 如果我强制客户端抛出I/O异常,服务器将检测到该异常并正确终止客户端线程(通过任务管理器(Windows)和系统监视器(Ubuntu)验证)。但是如果我模拟一个像那样“挂起”的I/O,即Thread.sleep(60*1000)或 private static Object lock=new Object() 然后,所有后续I/O操作(连接和数据传输)似乎都会阻

正如主题所示,我有一个服务器和一些客户端
服务器同时接受I/O连接(套接字连接中没有排队),但我有这个麻烦的问题,我不知道如何绕过它! 如果我强制客户端抛出
I/O异常
,服务器将检测到该异常并正确终止客户端线程(通过任务管理器(Windows)和系统监视器(Ubuntu)验证)。但是如果我模拟一个像
那样“挂起”的I/O,即
Thread.sleep(60*1000)

private static Object lock=new Object()

然后,所有后续I/O操作(连接和数据传输)似乎都会阻塞或等待“挂起”客户端终止。应用程序使用执行器服务,因此如果“挂起”客户端未在建议的时间限制内完成操作,则任务将超时,客户端将被迫退出。随后的“阻塞”I/O将恢复,但我想知道为什么当客户端“挂起”时,服务器不接受任何I/O连接或执行任何I/O操作?

注意:客户端线程在主服务器中进行,如下所示:

while (true) { 
   accept client connection;
   submit client task;
          ||
         \  /
          \/ 
   // ExecutorService here in the form 
   // spService.submit(new Callable<Tuple<String[], BigDecimal[]>>() { 
   // ... code ... }}).get(taskTimeout, taskTimeUnit);
   check task result & perform cleanup if result is null;
   otherwise continue;
}
while(true){
接受客户端连接;
提交客户端任务;
||
\  /
\/ 
//请在表格中填写此服务
//spService.submit(新的可调用(){
//获取(taskTimeout,taskTimeUnit);
检查任务结果,如果结果为空,则执行清理;
否则继续;
}
问题:

这很可能表明您的服务器同时接受客户端连接,但是,它只同步处理这些连接。这意味着,即使有一百万个客户端在任何给定的时间成功连接,如果其中任何一个需要很长时间(或挂断),它也会阻碍其他客户端

测试:

为了验证这一点:我将通过在客户端中添加Thread.sleep stations(1000)来切换客户端连接所需的时间

预期结果:

我相信您会看到,即使在客户端中添加一个Thread.sleep(1000)语句,所有其他连接的客户端也会延迟1000。

问题:

这很可能表明您的服务器同时接受客户端连接,但是,它只同步处理这些连接。这意味着,即使有一百万个客户端在任何给定的时间成功连接,如果其中任何一个需要很长时间(或挂断),它也会阻碍其他客户端

测试:

为了验证这一点:我将通过在客户端中添加Thread.sleep stations(1000)来切换客户端连接所需的时间

预期结果:


我相信您会看到,即使在您的客户端中添加一个Thread.sleep(1000)语句,所有其他连接的客户端也会延迟1000次。

我想我已经找到了问题的根源
我确实每个客户机模型使用一个线程,但我在本地运行测试,即在同一台机器上运行,这意味着所有测试都具有相同的IP
因此,每个客户机都与服务器分配了相同的IP!我猜这只会使服务器和客户端在端口号上有所不同,但由于每个客户端都映射到每个服务器连接的不同本地端口,因此服务器不应该阻塞。我已经确认每个客户端和服务器使用不同的I/O(比较引用),并且我使用
Stream
s到
BufferedReader
s&
PrintWriter
s包装它们的套接字,但是当一个客户端挂起时,所有其他客户端也挂起(因此,可能I/O通道确实是相同的??)
我将在另一台机器上进行测试,并与您核对结果!:)

编辑:确认了不稳定的行为。似乎即使是远程客户端,如果其中一个挂起,其他客户端也会挂起!:/
不知道,但我决心解决这个问题。只是这很奇怪,因为我很确定每个客户端使用一个线程(I/O不同,客户端套接字不同,IP似乎不是问题,我甚至将服务器中的每个客户端映射到我选择的本地端口…
如果我不能尽快找到解决方案,我可能会改用NIO

解决方案:解决问题
似乎
ExecutorService
必须在单独的线程中运行,否则如果客户端中的I/O被阻塞,所有I/O都会被阻塞
这很奇怪,因为我已经尝试了
Executors.newFixedThreadPool()
执行器。newCachedThreadPool()和客户端操作(也称为I/O)应在每个客户端的新线程中进行。

在任何情况下,我都使用了一个方法并包装了调用,这样每个客户端instace都将使用一个
final ExecutorService baseWorker=Executors.newSingleThreadExecutor().start()显式创建一个新线程这样每个线程都会在后台运行:)

我想我已经找到了问题的根源
我确实每个客户机模型使用一个线程,但我在本地运行测试,即在同一台机器上运行,这意味着所有测试都具有相同的IP
因此,每个客户机都与服务器分配了相同的IP!我猜这只会使服务器和客户端在端口号上有所不同,但由于每个客户端都映射到每个服务器连接的不同本地端口,因此服务器不应该阻塞。我已经确认每个客户端和服务器使用不同的I/O(比较引用),并且我使用
Stream
s到
BufferedReader
s&
PrintWriter
s包装它们的套接字,但是当一个客户端挂起时,所有其他客户端也挂起(因此,可能I/O通道确实是相同的??)
我将在另一台机器上进行测试,并与您核对结果!:)

编辑:确认了不稳定的行为。似乎即使是远程客户端,如果其中一个挂起,其他客户端也会挂起!:/
不知道,但我决心解决这个问题。只是
while (true) { 
   accept client connection;
   submit client task;
          ||
         \  /
          \/ 
   // ExecutorService here in the form 
   // spService.submit(new Callable<Tuple<String[], BigDecimal[]>>() { 
   // ... code ... }}).get(taskTimeout, taskTimeUnit);
   check task result & perform cleanup if result is null;
   otherwise continue;
}