Wenn mehrere Threads System.out.println (String) ohne Synchronisation aufrufen, kann die Ausgabe dann verschachtelt werden? Oder ist das Schreiben jeder Zeile atomar? Die API erwähnt die Synchronisation nicht, so dass dies möglich erscheint, oder wird eine verschachtelte Ausgabe durch Pufferung und / oder das VM-Speichermodell usw. verhindert?
BEARBEITEN:
Zum Beispiel, wenn jeder Thread Folgendes enthält:
System.out.println("ABC");
ist die Ausgabe garantiert:
ABC
ABC
oder könnte es sein:
AABC
BC
java
multithreading
synchronization
printstream
Ellen Spertus
quelle
quelle
DEBUG
Flag in den ersten Zeilen auf true gesetzt ist.Antworten:
Da in der API-Dokumentation weder die Thread-Sicherheit des
System.out
Objekts noch diePrintStream#println(String)
Methode erwähnt wird , können Sie nicht davon ausgehen, dass es thread-sicher ist .Es ist jedoch durchaus möglich, dass die zugrunde liegende Implementierung einer bestimmten JVM eine thread-sichere Funktion für die
println
Methode verwendet (z. B.printf
auf glibc ), sodass in Wirklichkeit die Ausgabe gemäß Ihrem ersten Beispiel garantiert wird (immerABC\n
dannABC\n
, niemals eingestreute Zeichen) gemäß Ihrem zweiten Beispiel). Beachten Sie jedoch, dass es viele JVM-Implementierungen gibt und diese nur die JVM-Spezifikation einhalten müssen, keine Konventionen außerhalb dieser Spezifikation.Wenn Sie unbedingt sicherstellen müssen, dass sich keine Druckanrufe wie beschrieben einmischen, müssen Sie den gegenseitigen Ausschluss manuell erzwingen, zum Beispiel:
public void safePrintln(String s) { synchronized (System.out) { System.out.println(s); } }
Natürlich ist dieses Beispiel nur eine Illustration und sollte nicht als "Lösung" verstanden werden. Es gibt viele andere Faktoren zu berücksichtigen. Zum Beispiel ist die
safePrintln(...)
obige Methode nur sicher, wenn der gesamte Code diese Methode verwendet und nichtsSystem.out.println(...)
direkt aufruft .quelle
safePrintln
ist nicht sicherer als eine einfache Druckanweisung. Nur weil Sie mit einem bestimmten Objekt synchronisieren, erhalten Sie keine Sicherheit. Sie sind nur threadsicher, wenn der gesamte Code, der auf die Ressource zugreift, auf demselben Objekt synchronisiert wird. Wenn Sie jedoch davon ausgehen, dass alle Code-Aufruf-Druckmethoden auf derSystem.out
Instanz synchronisiert werden, kehren Sie zu Ihrem Ausgangspunkt zurück. Außerdem ist Ihr Code fehlerhaft, da er dieSystem.out
Variable zweimal liest . Wenn jemandSystem.setOut(…)
direkt zwischen diesen beiden Lesevorgängen anruft , wird die Synchronisierung unterbrochen.System.out.println(s);
ohne aufrufensynchronized (System.out)
kann. Diese Methode funktioniert also nur, wenn alle Threads die Konvention einhalten, diese Methode zu verwenden (odersynchronized
alleine ausführen), anstattSystem.out
direkt darauf zuzugreifen, und niemand anruftsetOut
. Aus diesem Grund wird die Synchronisierung normalerweise mit der Kapselung kombiniert, sodass kein direkter Zugriff auf die Ressource möglich ist.Der OpenJDK-Quellcode beantwortet Ihre Frage:
public void println(String x) { synchronized (this) { print(x); newLine(); } }
Referenz: http://hg.openjdk.java.net/jdk6/jdk6/jdk/file/39e8fe7a0af1/src/share/classes/java/io/PrintStream.java
quelle
Solange Sie die
OutputStream
Via nicht ändernSystem.setOut
, ist sie threadsicher.Obwohl es threadsicher ist, können Sie viele Threads so schreiben lassen,
System.out
dassThread-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");
kann lesen
1 2 A 3 B C
unter anderen Kombinationen.
Um Ihre Frage zu beantworten:
Wenn Sie in schreiben
System.out
- es erhält eine Sperre für dieOutputStream
Instanz -, wird es in den Puffer schreiben und sofort leeren.Sobald das Schloss
OutputStream
freigegeben ist , wird das geleert und beschrieben. Es würde keine Instanz geben, in der Sie verschiedene Zeichenfolgen wie verbunden hätten1A 2B
.Bearbeiten, um Ihre Bearbeitung zu beantworten:
Das würde mit nicht passieren
System.out.println
. Da dasPrintStream
die gesamte Funktion synchronisiert, füllt es den Puffer und spült ihn dann atomar. Jeder neue Thread, der hereinkommt, hat jetzt einen neuen Puffer, mit dem er arbeiten kann.quelle
synchronized
Angenommen, Sie haben zwei Threads, einen, der gedruckt wird,
"ABC"
und einen, der gedruckt wird"DEF"
. Sie werden niemals eine solche Ausgabe erhalten:ADBECF
aber Sie könnten auch keine bekommenoder
quelle
ABC
obenDEF
oderDEF
obenABC