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

问题描述 投票:0回答:4

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

编辑:

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

System.out.println("ABC");

输出保证为:

ABC
ABC

或者可能是:

AABC
BC
java multithreading synchronization printstream
4个回答
78
投票

由于 API 文档没有提及

System.out
对象 上的线程安全性,也没有提到
PrintStream#println(String)
方法
你不能假设它是线程安全的

但是,特定 JVM 的底层实现完全有可能对

println
方法使用线程安全函数(例如 glibc 上的
printf
),因此,实际上,输出将根据您的要求得到保证第一个示例(始终是
ABC\n
然后是
ABC\n
,第二个示例中绝不会散布字符)。 但请记住,有很多 JVM 实现,它们只需要遵守 JVM 规范,而不是该规范之外的任何约定。

如果您绝对必须确保不会像您所描述的那样散布 println 调用,那么您必须手动强制执行互斥,例如:

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

当然,这个例子只是一个说明,不应该被视为“解决方案”;还有许多其他因素需要考虑。例如,上面的

safePrintln(...)
方法仅当 all 代码使用该方法并且没有任何内容直接调用
System.out.println(...)
时才是安全的。


28
投票

OpenJDK 源代码回答了您的问题:

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

参考:https://hg.openjdk.org/jdk6/jdk6/jdk/file/39e8fe7a0af1/src/share/classes/java/io/PrintStream.java#l772


15
投票

只要您不通过

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
投票

为了澄清一下,假设您有两个线程,一个打印

"ABC"
,另一个打印
"DEF"
。你永远不会得到这样的输出:
ADBECF
,但你可以得到其中一个

ABC
DEF 

DEF
ABC
© www.soinside.com 2019 - 2024. All rights reserved.