System.out.println的多线程输出是否是交错的

时间:2012-02-27 03:23:23

标签: java multithreading synchronization printstream

如果多个线程在没有同步的情况下调用System.out.println(String),输出是否可以交错?或者每行写入原子? API没有提到同步,所以这似乎是可能的,或者是缓冲和/或VM内存模型等阻止了交错输出?

编辑:

例如,如果每个线程包含:

System.out.println("ABC");

输出保证为:

ABC
ABC

或者可能是:

AABC
BC

4 个答案:

答案 0 :(得分:56)

由于API文档未在System.out object上提及线程安全性,PrintStream#println(String) method 也不能认为它是线程安全的

但是,特定JVM的底层实现完全有可能使用println方法的线程安全函数(例如printf on glibc),这样,实际上,输出将得到保证根据您的第一个示例(始终为ABC\n然后ABC\n,您的第二个示例中不会出现散布的字符。但请记住,有许多JVM实现,并且只需要遵守JVM规范,而不是该规范之外的任何约定。

如果您绝对必须确保没有println调用会在您描述时散布,那么您必须手动强制执行互斥,例如:

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

当然,这个例子只是一个例子,不应被视为“解决方案”;还有许多其他因素需要考虑。例如,如果所有代码使用该方法且没有任何内容直接调用safePrintln(...),则上述System.out.println(...)方法才是安全的。

答案 1 :(得分:18)

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

答案 2 :(得分:9)

只要您不通过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同步整个函数,它将填充缓冲区然后以原子方式对其进行刷新。任何进入的新线程现在都有一个新的缓冲区可供使用。

答案 3 :(得分:2)

只是为了澄清,说你有两个线程,一个打印"ABC",另一个打印"DEF"。你永远不会得到这样的输出:ADBECF,但你可以得到

ABC
DEF 

DEF
ABC