Чередуется ли многопоточный вывод из System.out.println - PullRequest
65 голосов
/ 27 февраля 2012

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

System.out.println("ABC");

, гарантированно будет вывод:

ABC
ABC

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

AABC
BC

Ответы [ 4 ]

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

Поскольку в документации 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(...) безопасен только в том случае, если код all использует этот метод и ничего не вызывает System.out.println(...) напрямую.

18 голосов
/ 06 мая 2012

Исходный код 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

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

Пока вы не измените 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 синхронизирует всю функцию, он заполнит буфер и затем очистит его атомарно. Теперь у любого нового потока будет новый буфер для работы.

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

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

ABC
DEF 

, либо

DEF
ABC
...