有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

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

private static Object lock = new Object();

synchronized(lock) {
   while (true) {
      try {
         lock.wait();
      } catch (InterruptedException e) {
         /* Foo */
      }
   }
} 

然后,所有后续I/O操作(连接和数据传输)似乎都会阻塞或等待“挂起”客户端终止。应用程序使用ExecutorService,因此如果“挂起”客户端未在建议的时间限制内完成操作,则任务将超时,客户端将被迫退出。随后的“阻塞”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;
}

共 (2) 个答案

  1. # 1 楼答案

    问题是:

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

    测试:

    为了验证这一点:我将通过添加线程来切换客户端连接所需的时间。在你的客户身上安睡(1000)

    预期结果:

    我相信你会看到,即使添加一个线程。客户机中的sleep(1000)语句将所有其他连接的客户机延迟1000次

  2. # 2 楼答案

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

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

    解决方案:解决了问题
    似乎ExecutorService必须在单独的线程中运行,否则如果客户端中的I/O被阻塞,所有I/O都会被阻塞
    这很奇怪,因为我已经尝试了Executors.newFixedThreadPool(<nThreads>);Executors.newCachedThreadPool();,并且客户机操作(也称为I/O)应该在每个客户机的新线程中进行
    在任何情况下,我都使用了一个方法并包装了调用,这样每个客户端instace都将使用final ExecutorService baseWorker = Executors.newSingleThreadExecutor();,并且每次都使用<Thread instance>.start();显式创建一个新线程,这样每个线程都将在后台运行:)