Вопрос-ответ

Is multi-thread output from System.out.println interleaved

Многопоточный вывод из System.out.println с чередованием

Если несколько потоков вызывают System.out.println(String) без синхронизации, может ли чередоваться вывод? Или запись каждой строки выполняется атомарно? В API не упоминается синхронизация, поэтому это кажется возможным, или чередующийся вывод предотвращается буферизацией и / или моделью памяти виртуальной машины и т.д.?

Редактировать:

Например, если каждый поток содержит:

System.out.println("ABC");

гарантируется ли, что вывод будет:

ABC
ABC

или это может быть:

AABC
BC
Переведено автоматически
Ответ 1

Поскольку в документации API нет упоминания о потокобезопасности для System.out объекта, равно как и в PrintStream#println(String) методе вы не можете предположить, что он потокобезопасен.

Однако вполне возможно, что базовая реализация конкретной JVM использует потокобезопасную функцию для println метода (например, printf на glibc), так что на самом деле вывод будет гарантирован для вашего первого примера (всегда ABC\n тогда ABC\n, никогда не чередующиеся символы для вашего второго примера). Но имейте в виду, что существует множество реализаций JVM, и от них требуется только соблюдение спецификации JVM, а не каких-либо соглашений за пределами этой спецификации.

Если вы абсолютно должны гарантировать, что никакие вызовы println не будут чередоваться, как вы описываете, тогда вы должны принудительно выполнить взаимное исключение вручную, например:

public void safePrintln(String s) {
synchronized (System.out) {
System.out.println(s);
}
}

Конечно, этот пример является лишь иллюстрацией и его не следует воспринимать как "решение"; необходимо учитывать множество других факторов. Например, safePrintln(...) приведенный выше метод безопасен только в том случае, если весь код использует этот метод и ничто не вызывает System.out.println(...) напрямую.

Ответ 2

Исходный код OpenJDK отвечает на ваш вопрос:

public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}

Ссылка: http://hg.openjdk.java.net/jdk6/jdk6/jdk/file/39e8fe7a0af1/src/share/classes/java/io/PrintStream.java

Ответ 3

Пока вы не меняете OutputStream via System.setOut, это потокобезопасно.

Хотя это потокобезопасно, у вас может быть много потоков, записывающих в System.out так, что

Thread-1
System.out.println("A");
System.out.println("B");
System.out.println("C");
Thread-2
System.out.println("1");
System.out.println("2");
System.out.println("3");

можно прочитать

1
2
A
3
B
C

среди других комбинаций.

Итак, чтобы ответить на ваш вопрос:

Когда вы записываете в System.out – он получает блокировку для OutputStream экземпляра - затем он записывает в буфер и немедленно сбрасывает.

Как только он снимает блокировку, OutputStream очищается и записывается в. Не было бы случая, когда у вас были бы разные строки, соединенные подобным образом 1A 2B.

Отредактируйте, чтобы ответить на вашу правку:

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

Ответ 4

Просто для пояснения, предположим, у вас есть два потока, один, который печатает "ABC", а другой, который печатает "DEF". Вы никогда не получите такой вывод: ADBECF, но вы могли бы получить либо

ABC
DEF

или

DEF
ABC
java multithreading