Чередуется ли многопоточный вывод из System.out.println
Если несколько потоков вызывают System.out.println(String) без синхронизации, можно ли чередовать выходные данные? Или запись каждой строки атомарна? API не упоминает синхронизацию, так что это кажется возможным, или чересстрочный вывод предотвращается буферизацией и/или моделью памяти виртуальной машины и т.д.?
РЕДАКТИРОВАТЬ:
Например, если каждый поток содержит:
System.out.println("ABC");
гарантированно будет ли вывод:
ABC
ABC
или это может быть:
AABC
BC
Ответы
Ответ 1
Поскольку в документации API нет упоминания о безопасности потоков на System.out
object, а также PrintStream#println(String)
method, вы не можете предположить, что это поточно-безопасный.
Однако вполне возможно, что базовая реализация конкретной 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
через 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