system.out.println()在多线程下会阻塞主线程,因其方法被synchronized(this)修饰,所有线程竞争同一printstream全局锁;高并发时大量线程blocked,主线程调用该方法即参与争锁,一旦锁被长时占用(如i/o慢、重定向等),便会真实停住无法推进。

System.out 不会“隐式锁死整个主线程”,但它会在高并发下严重阻塞所有竞争该资源的线程——包括主线程,只要它调用了 System.out.println() 或其他输出方法。
根本原因:PrintStream 的同步锁机制
System.out 是一个 PrintStream 实例,其 println()、print() 等方法内部均被 synchronized(this) 修饰。也就是说,每次调用都需获取当前 PrintStream 对象的 intrinsic lock(内置锁)。
由于 System.out 是 static final 的全局单例,所有线程共享同一个锁对象。当并发量大时:
- 大量线程排队等待获取同一把锁
- 先拿到锁的线程执行 I/O(可能因缓冲区刷新、终端响应慢、重定向到文件等变慢)
- 其余线程全部进入
BLOCKED状态,持续等待锁释放
为什么主线程也会被卡住?
主线程和其他线程没有特权区别。只要它执行了如下任意一行代码,就会参与锁竞争:
System.out.println("init done");System.out.format("count: %d", count);- 甚至在异常堆栈打印中隐式触发(如未捕获异常自动调用
printStackTrace(),它也走System.err,而err同样是同步的PrintStream)
一旦主线程在关键路径(如启动逻辑、健康检查、定时任务触发点)中调用输出,又恰逢其他线程正长时间持有锁(例如某次 println 因底层 FileOutputStream.write() 阻塞),主线程就会真实地停在那行代码上,无法继续推进。
典型放大场景
以下情况会让问题更明显:
- 日志被重定向到慢速设备:如写入 NFS 挂载目录、磁盘满、或 stdout 被管道传给一个处理缓慢的 shell 进程
-
多线程高频打点:比如每个请求都
System.out.println("req_id=" + id),100+ 线程争一把锁,吞吐骤降 -
与自定义锁嵌套:如线程先持有了业务锁 A,再调用
System.out.println();另一线程先持System.out锁,再试图获取锁 A —— 此时就构成真·死锁(非单纯阻塞)
不是“隐式”,而是明确且可追踪的同步行为
这不是 JVM 的黑盒机制,而是公开的 JDK 源码行为:
- JDK 中
PrintStream.println(String)方法体就是synchronized(this) { print(x); newLine(); } - 线程堆栈中清晰可见
at java.io.PrintStream.println(PrintStream.java:805) - waiting to lock - 用
jstack可直接观察到数十甚至数百线程 BLOCKED 在同一地址上











