如果多个线程在没有同步的情况下调用 System.out.println(String),输出可以交错吗?还是每一行的写入都是原子的?API没有提到同步,所以这似乎是可能的,或者缓冲和/或VM内存模型等阻止了交错输出?
编辑:
例如,如果每个线程包含:
System.out.println("ABC");
是保证输出为:
ABC
ABC
或者可能是:
AABC
BC
如果多个线程在没有同步的情况下调用 System.out.println(String),输出可以交错吗?还是每一行的写入都是原子的?API没有提到同步,所以这似乎是可能的,或者缓冲和/或VM内存模型等阻止了交错输出?
编辑:
例如,如果每个线程包含:
System.out.println("ABC");
是保证输出为:
ABC
ABC
或者可能是:
AABC
BC
由于 API 文档没有提及System.out
对象的线程安全性,因此您不能假设它是线程安全的PrintStream#println(String)
方法 。
但是,特定 JVM 的底层实现完全有可能对该println
方法使用线程安全函数(例如printf
在 glibc 上),因此,实际上,每个您的第一个示例都会保证输出(总是ABC\n
then ABC\n
,从不散布字符根据你的第二个例子)。但请记住,有很多 JVM 实现,它们只需要遵守 JVM 规范,而不是该规范之外的任何约定。
如果您绝对必须确保不会像您描述的那样散布 println 调用,那么您必须手动强制互斥,例如:
public void safePrintln(String s) {
synchronized (System.out) {
System.out.println(s);
}
}
当然,这个例子只是一个说明,不应被视为“解决方案”;还有许多其他因素需要考虑。例如,safePrintln(...)
上面的方法只有在所有代码都使用该方法并且没有直接调用的情况下才是安全的System.out.println(...)
。
OpenJDK 源代码回答了您的问题:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
只要您不更改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
同步整个函数,它将填充缓冲区,然后以原子方式刷新它。任何进入的新线程现在都将有一个新的缓冲区可供使用。
只是为了澄清,假设你有两个线程,一个打印"ABC"
,另一个打印"DEF"
。你永远不会得到这样的输出:ADBECF
,但你可以得到
ABC
DEF
或者
DEF
ABC