我们在单个物理主机上运行了多个应用程序实例(每个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)
答案 0 :(得分:2)
发现所有线程都在日志语句中被BLOCKED,等待对println对象的锁定。而其他一些对象已经锁定了println。
PrintStream
类System.out
和System.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来提高驱动器的速度。