2

我的程序运行了一段时间(大约 20 秒),之后它发送的消息越来越少,直到它停止。我做了一个线程转储,它将我指向线程停止的以下代码行:

if(s.isClosed() || !s.isConnected() || s.isInputShutdown() || s.isOutputShutdown() || out == null || out.checkError()) {

这是我的线程转储的一部分

    "class xxx.xxx.xxx" prio=10 tid=0x000000000200d800 nid=0x6e7f waiting for monitor entry [0x00007f63172b6000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at java.io.PrintWriter.flush(PrintWriter.java:291)
        - waiting to lock <0x00000000ec810218> (a java.io.BufferedWriter)
        at java.io.PrintWriter.checkError(PrintWriter.java:330)

此线程转储信息指出问题出在 Printwriter.checkError() 中。但我不明白为什么它会停在这里。

以下是其上下文中的代码。(接收者是一个Map<Socket, PrintWriter>

public void parseMessage(final byte[] bytes) {
    synchronized (receivers) {
        for (final Socket s : receivers.keySet()) {
            final PrintWriter out = receivers.get(s);
            if(s.isClosed() || !s.isConnected() || s.isInputShutdown() || s.isOutputShutdown() || out == null || out.checkError()) {
                receivers.remove(s);
                failedToConnect(s.getInetAddress().getHostAddress(), s.getPort());
                continue;
            }
            queue.add(out, bytes);
        }
    }
}

1 月 14 日更新(格林威治标准时间 10:00+1):

替换PrintWriterBufferedWriter

1 月 14 日更新(格林威治标准时间 16:00+1):

我进行了更多测试,结果发现某些网络/阅读器之间的某种通信问题正在造成麻烦。该应用程序正在向其内部网络中的一些读者发送数据,并发送给一些外部网络中的一些读者(通过互联网)。究竟是什么原因导致这一点仍然未知,但有一些线索。几次重新启动后,我可以看到两个网络之间的连接没有关闭,因此连接保持打开状态。我仍然不知道这里到底发生了什么,但在我看来这是某种网络问题。在服务器端,连接保持在“FIN_WAIT1”中,在阅读器端保持“ESTABLISHED”。即使已发送信号关闭连接,阅读器也不会发出关闭连接的信号。

4

2 回答 2

1
if(s.isClosed() || !s.isConnected() || s.isInputShutdown() || s.isOutputShutdown() || out == null || out.checkError())

这是一系列几乎是徒劳的测试。

  • 如果套接字已关闭,您根本不应该在这部分代码中。
  • 如果套接字未连接,同上。
  • 如果您关闭了套接字以进行输入,这并不意味着您不能写。
  • 如果您关闭了输出套接字,您不应该在这部分代码中。
  • 如果out为 null 你不应该在这部分代码中。
  • 测试checkError()很好,但你根本不应该通过PrintWriter网络使用,因为它会吞下异常。最好使用BufferedWriter并让异常告诉您发生错误的事情,而不是像您在这里所做的那样在下一次写入时告诉您。

您还需要了解,Socket.isClosed()仅告诉您是否关闭Socket.类似地,Socket.isConnected()仅告诉您已连接Socket,或已通过构造函数接收它已连接,或者ServerSocket.accept()。同样适用isInputShutdown()isOutputShutdown().他们只告诉你你对这些方法做了什么Socket.,这些方法都没有告诉你关于连接状态的任何事情。

此线程转储信息指出问题出在 Printwriter.checkError() 中。但我不明白为什么它会停在这里。

因为,正如堆栈跟踪也告诉你的那样,它正在调用flush().If that blocks 这意味着你已经超出了阅读器,并且 TCP 正在等待他读取一些数据以释放他的套接字接收缓冲区中的缓冲区空间,从而释放你的套接字发送缓冲区。在此之前,底层证券send()将阻塞。除了加快阅读速度外,您无能为力。

于 2013-02-14T00:34:36.090 回答
0

当一个线程被阻塞时,它很可能正在等待 I/O 请求 看看这个链接它与你遇到的非常相似 how-to-analyze-java-thread-dumps

于 2013-02-13T18:43:25.380 回答