Несколько потоков, использующих System.out.println в Java - PullRequest
5 голосов
/ 27 февраля 2012

У меня есть многопоточное приложение Java, которое выводит информацию о сообщении, которое оно получает, на консоль для целей отладки. Каждый раз, когда приложение получает сообщение, оно будет вызывать System.out.println(String) в сообщении.

Проблема, с которой я столкнулся, заключается в том, что если приложение заполняется сообщениями, System.out.println() печатает ошибочную информацию (например, информацию о старом буфере). Это заставляет меня задуматься о том, существует ли проблема с многопоточностью, когда несколько потоков одновременно вызывают функцию println и неправильно очищают буфер.

В моей основной программе (ветке) есть что-то с эффектом:

while(iterator.hasNext())
{
    SelectionKey key = iterator.next();

    channel.receive(buffer);     // The buffer is a ByteBuffer.
    buffer.flip();

    new Thread(new ThreadToPrintTheMessage(buffer)).start();

    buffer.clear();

    iterator.remove();
}

В моей теме есть что-то с эффектом:

@Override
public void run()
{
    System.out.println(message);
    System.out.flush();   // I have better results with this.  But, it doesn't
                          // fully resolve the issue.
}

Есть ли для меня простой способ одновременной печати нескольких потоков на консоли без буферов, содержащих старую информацию?

Спасибо

РЕДАКТИРОВАТЬ: обновил код в главном потоке, чтобы лучше представлять, что делает моя программа.

Ответы [ 4 ]

2 голосов
/ 27 февраля 2012

У меня нет времени, чтобы проверить источник println, будьте уверены, что он всегда безопасен для потоков (вы могли бы, если бы захотели), но вы уверены, что ваш println неверен?Вполне может быть, что код выполняется в совсем другое время, чем вы думаете.Потоки часто зацикливаются на блокировках или просто забываются планировщиком, поэтому то, что вы думаете, должно запускать A, B, C, D, может запускать B, C, D, A. Тогда вы удивляетесь, почему println испортился, печатая последниеты думаешь побежал первым.И это действительно простой пример.Разница между тем, что вы ожидаете от многопоточности, и тем, что происходит, может быть поразительной.Как правило, чем выше отношение потока к сердечнику, тем хуже оно.Одноядерные машины всегда делают все противоположное тому, что вы ожидаете.

И вам даже не нужно несколько потоков, чтобы иметь эту проблему.Мои первые потрясения были связаны с очередями событий (в Windows 3.1), которые не разделяли мои взгляды на время запуска.Мне потребовалось некоторое время, чтобы понять, что сообщения были зашифрованы, потому что ОС имела совершенно другое представление о том, как они должны работать, чем я.

В System.out.println и flush вполне могут быть некоторые тонкостио котором я не знаю, но даже когда у вас все это работает, имейте в виду, что эти темы имеют довольно противоречивые собственные умы.Даже Logger не решит все ваши проблемы.

2 голосов
/ 27 февраля 2012

Вот пример кода для решения проблемы:

while(iterator.hasNext())
{
    SelectionKey key = iterator.next();

    channel.receive(buffer);     // The buffer is a ByteBuffer.
    buffer.flip();
    byte[] bytes = new byte[buffer.limit()];  // copy buffer contents to an array
    buffer.get(bytes);
    // thread will convert byte array to String
    new Thread(new ThreadToPrintTheMessage(bytes)).start();

    buffer.clear();

    iterator.remove();
}
2 голосов
/ 27 февраля 2012
synchronized (System.out) {
    System.out.println(message);
    System.out.flush();
}
1 голос
/ 27 февраля 2012

Для этой цели вы должны использовать Java.util.logging или какую-либо другую инфраструктуру журналирования.

http://docs.oracle.com/javase/1.4.2/docs/api/java/util/logging/Logger.html

import java.util.logging.Logger;
....
Logger log = Logger.getLogger("com.something.something");
....
log.log(Level.Info, "Message");
...