在Java中的println语句中阻塞了线程

时间:2013-11-03 19:50:14

标签: java multithreading thread-dump blocked-threads

我们在单个物理主机上运行了多个应用程序实例(每个tomcat服务器一个)。 应用程序做了不错的记 最近我们发现一些应用程序变慢或挂起并需要重新启动。 在线程转储中,发现所有线程在日志语句中都是BLOCKED,等待println对象上的锁定。而其他一些对象已经锁定println。 但我无法弄清楚为什么其他线程没有释放锁定println对象? 我粘贴了一些线程转储快照:

BLOCKED线程转储:

java.lang.Thread.State: BLOCKED (on object monitor)
    at java.io.PrintStream.println(PrintStream.java:755)
    - waiting to lock <0x00000007830097e0> (a java.io.PrintStream)
    at org.apache.tomcat.util.log.SystemLogHandler.println(SystemLogHandler.java:238)
    at com.webaroo.smsnew.common.SMSUtils.log(SMSUtils.java:167)
    at com.webaroo.smsnew.common.SMSUtils.log(SMSUtils.java:113)

获取println日志的线程的线程转储。

java.lang.Thread.State: BLOCKED (on object monitor)
    at java.nio.CharBuffer.wrap(CharBuffer.java:350)
    at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:246)
    at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:106)
    - locked <0x00000007830098f0> (a java.io.OutputStreamWriter)
    at java.io.OutputStreamWriter.write(OutputStreamWriter.java:190)
    at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:111)
    - locked <0x00000007830098f0> (a java.io.OutputStreamWriter)
    at java.io.PrintStream.write(PrintStream.java:476)
    - locked <0x00000007830097e0> (a java.io.PrintStream)
    at java.io.PrintStream.print(PrintStream.java:619)
    at java.io.PrintStream.println(PrintStream.java:756)
    - locked <0x00000007830097e0> (a java.io.PrintStream)
    at org.apache.tomcat.util.log.SystemLogHandler.println(SystemLogHandler.java:238)

1 个答案:

答案 0 :(得分:2)

  

发现所有线程都在日志语句中被BLOCKED,等待对println对象的锁定。而其他一些对象已经锁定了println。

PrintStreamSystem.outSystem.err都是同步类。任何println(...)方法在执行打印之前会锁定,以便多个线程不会获得重叠的输出行。

仅仅因为线程转储显示在该位置阻塞的线程并不意味着它被挂起。它可能只是意味着它是您应用程序中最慢的部分。更多的线程转储将显示其他线程正在进入println()但也被阻止。如果有多个线程被阻塞,那么输出IO(可能是控制台)就会降低你的应用程序速度。您应该减少日志方法的数量或减少每条消息中的信息量。如果这没有用,那么你将不得不考虑其他输出机制。

如果您需要输出,那么每个线程都可以写入自己的BufferedWriter包裹FileWriter。或者你可以让一个线程执行实际输出,所有其他线程将它们的消息添加到BlockingQueue并且一个编写器将消息排队,然后将其写入不锁定和缓冲的BufferedWriter它的I / O.

private final BlockingQueue<String> messageQueue
      = new ArrayBlockingQueue<String>();
...
// add a message to the queue
messageQueue.add("some log output here: " + someValue);
...
// writer thread
private class LogThread implements Runnable {
    public void run() {
       BufferedWriter writer =
            new BufferedWriter(new FileWriter("/var/log/some_log_file.txt"));
       try {
          while (!Thread.currentThread().isInterrupted()) {
             String msg = messageQueue.take();
             writer.write(msg);
          }
       } finally {
          writer.close();
       }
    }
}

但是,您可能会发现,即使您有一个线程通过缓冲流写入,也超出了硬盘的IO带宽。您可以在此时尝试gziped流,但通常会按顺序重新评估输出。你能减少输出线的数量吗?你可以在内存中保留某种计数器并经常转储它。如果确实需要日志输出,那么您可能需要考虑通过移动到SSD来提高驱动器的速度。