Несколько потоков, использующих System.out.println в Java
У меня есть многопоточное приложение 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 ответа
Вот пример кода для решения проблемы:
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();
}
synchronized (System.out) {
System.out.println(message);
System.out.flush();
}
У меня нет времени, чтобы проверить исходный код println, будьте уверены, что он всегда безопасен для потоков (вы могли бы, если бы захотели), но вы уверены, что ваш println неверен? Вполне может быть, что код выполняется в совсем другое время, чем вы думаете. Потоки часто зацикливаются на блокировках или просто забываются планировщиком, поэтому то, что вы думаете, должно запускать A, B, C, D, может запускать B, C, D, A. Тогда вы удивляетесь, почему println испортился, печатая последние ты думаешь побежал первым. И это действительно простой пример. Разница между тем, что вы ожидаете от многопоточности, и тем, что происходит, может быть поразительной. Как правило, чем выше отношение потока к сердечнику, тем оно хуже. Одноядерные машины всегда делают все противоположное тому, что вы ожидаете.
И вам даже не нужно несколько потоков, чтобы иметь эту проблему. Мои первые потрясения были связаны с очередями событий (в Windows 3.1), которые не разделяли мои взгляды на время запуска. Мне потребовалось некоторое время, чтобы понять, что сообщения были зашифрованы, потому что ОС имела совершенно другое представление о том, как они должны работать, чем я.
С System.out.println и flush могут быть некоторые тонкости, о которых я не знаю, но даже когда вы все это заработаете, помните, что эти потоки имеют свои собственные противоположные взгляды. Даже Logger не решит все ваши проблемы.
Для этой цели вы должны использовать 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");