Поток заблокирован в операторе println в Java

У нас запущено несколько экземпляров приложения (по одному на сервер tomcat) на одном физическом хосте. Приложение делает приличную регистрацию. Недавно мы заметили, что некоторые приложения работают медленнее или зависают и требуют перезапуска. В дампе потока обнаружено, что все потоки были заблокированы в операторе журнала, ожидая блокировки на println объект. и какой-то другой объект уже заблокирован println, Но я не мог понять, почему другой поток не снял блокировку на println объект? Я вставил несколько снимков дампа потока:

ЗАБЛОКИРОВАНА Нить дампа:

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)

Поток дампов потока, который взял журнал печати.

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 ответ

Обнаружено, что все потоки были заблокированы в операторе журнала, ожидая блокировки на объекте println. и другой объект уже заблокирован в println.

PrintStream класс, который System.out а также System.errявляются синхронизированными классами. любой println(...) заблокируйте метод перед печатью, чтобы несколько потоков не перекрывали выходные строки.

То, что дамп потока показывает поток, заблокированный в этом месте, не означает, что он завис. Это просто означает, что это самая медленная часть вашего приложения. Другие дампы потоков покажут, что другие потоки делают это в println() но и там заблокирован. Если там заблокировано несколько потоков, то вывод IO (возможно, на консоль) замедляет работу вашего приложения. Вы должны уменьшить количество методов журнала или уменьшить количество информации в каждом сообщении. Если это не поможет, вам придется рассмотреть другие механизмы вывода.

Если вам нужен вывод, то каждый поток может писать в свой BufferedWriter обертывание FileWriter например. Или вы можете сделать так, чтобы один поток выполнял фактический вывод, а все остальные потоки добавляли свои сообщения в BlockingQueue и один писатель выдает сообщение в очередь, а затем записывает его в BufferedWriter который не блокирует и буферизует свой ввод / вывод.

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();
       }
    }
}

Однако вы можете обнаружить, что даже если у вас есть один поток, записывающий через буферизованный поток, что вы превышаете пропускную способность ввода-вывода вашего жесткого диска. Вы можете попробовать gziped поток в этот момент, но обычно переоценка результатов в порядке. Можете ли вы уменьшить количество выходных линий? Можете ли вы сохранить какие-то счетчики в памяти и делать это время от времени. Если вам действительно нужен вывод журнала, вам, возможно, придется подумать об увеличении скорости вашего диска путем перехода на SSD.

Другие вопросы по тегам