System.err.println在Java中的线程压力下失败

时间:2017-05-14 10:45:46

标签: java multithreading thread-safety

我正在使用java编写程序,我使用一些错误打印语句进行调试。 我的程序生成大约2000个线程。程序运行正常,直到大量线程访问此语句:

System.err.println("Some error message");

当发生这种情况时,我的一个线程成功地设法访问println函数,而其他线程具有状态:

  

JVM中的状态:等待同步块

深入研究调试语句我注意到设法访问println函数的线程在此函数处停止:

private native void writeBytes(byte b[], int off, int len , boolean append) throws IOException;

它有以下堆栈跟踪:

  

java.io.FileOutputStream.write(FileOutputStream.java:327)

     

java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)

     

java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)

     

java.io.PrintStream.write(PrintStream.java:482)

     

sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)

     

sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)

     

sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)

     

java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)

     

java.io.PrintStream.write(PrintStream.java:527)

     

java.io.PrintStream.print(PrintStream.java:669)

     

java.io.PrintStream.println(PrintStream.java:806)

     

fetcher.responseHandler.ExtendedResponseHandler500.handleResponse(ExtendedResponseHandler500.java:20)

     

fetcher.FetchWorker.run(FetchWorker.java:79)

     

java.lang.Thread.run(Thread.java:745)

当其他线程在println函数的第一行停止时(在java核心代码中):

synchronized(this)

这个问题是由我引起的吗?或者此错误与JVM有关吗?我能解决这个问题吗?

2 个答案:

答案 0 :(得分:2)

最可能的原因是进程的输出流未被父进程占用,因此stdout缓冲区填满,然后对System.err.println的下一次调用将永远挂起。

当一个进程用于启动另一个进程时,这很常见,但是没有设置“刷新”线程来排空子进程的stdout和stderr流。

请注意,这与“线程”没有任何关系 - 但是启动多个线程肯定会增加生成错误的速率(如果由于下游争用而导致其他错误,可能会导致更多的总错误)这意味着您的输出缓冲区填满得更快并且更早挂起。

答案 1 :(得分:-1)

2000个线程等待获取println调用的锁定是完全正常的。

您的堆栈跟踪显示您收到一些HTTP 500错误。可能大多数你的线程都遇到了这个错误,现在全部都在报告标准错误。你所看到的是你的问题的结果,而不是原因。

2000线程是一个疯狂的数字,它不会改善任何合理情况下的性能,并且很可能会降低性能。从类似于4的东西开始,看看是否逐渐加倍该值会给你带来任何改进。 JVM可以处理这么多线程(所以这不是你的问题的根源),但它只是没用。使用更多JVM无法解决问题(可能是简单的HTTP 500和/或网络超时)。 另请检查服务器端日志。

如果你需要最大化性能(在真正的高并发场景中)考虑asynchio,但正常IO对于所有常见情况都非常好,并且在这里似乎很好。

<强>更新 我的想法是:

  1. 一个线程进行远程调用
  2. 收到500错误
  3. 它加入该行以报告其他2000个线程背后的错误
  4. 它成功并回到第1点
  5. 步骤3可能需要花费很多时间,所以即使采用多个线程转储,您也会看到始终出现的相同线程(我的意思是您需要非常幸运地在步骤1中看到Thread-1352或2)。我假设您检查了线程名称,并且我们正在讨论的锁定线程始终是相同的。 你看到程序&#34;冻结&#34; (它冻结了,对吗?)或者一切都还在吗?你花了多少线程转储,中间有多少时间?