根本原因是system.out基于带缓冲的printstream,默认延迟刷新;println()因自动刷新和换行通常立即显示,print()则需手动flush。

System.out 输出延迟,根本原因在于它背后是带缓冲的 PrintStream,不是“写了就立刻显示”。默认情况下,数据先写进内存缓冲区,等满足特定条件才真正送到控制台——这本是为了提升 I/O 效率,但对调试、实时日志或交互式输出来说,就成了“卡住”的源头。
为什么 print() 不立即显示,而 println() 通常可以?
PrintStream 的自动刷新(auto-flush)只对少数方法生效:println()、printf()、format()。它们内部会写入换行符,并在 autoFlush = true(默认)时触发 flush。print() 没有换行动作,也不触发自动刷新,内容就一直留在缓冲区里,直到缓冲区满、程序结束,或者你手动干预。
- ✅ System.out.println("ok") → 一般立刻可见(自动 flush + 换行)
- ⚠️ System.out.print("loading...") → 很可能不显示,除非后续跟 println() 或调用 flush()
- ✅ System.out.print("loading..."); System.out.flush(); → 强制刷出,立刻看到
重定向后输出“消失”或严重延迟
当你用 System.setOut() 把输出重定向到文件、ByteArrayOutputStream 或 IDE 模拟流时,PrintStream 会检测底层 OutputStream 是否连接终端(tty)。如果不是(比如写文件),它就自动降级为全缓冲模式(block-buffered),缓冲区大小通常是 8KB。这意味着:哪怕只输出一行,也可能卡住几秒甚至更久,直到缓冲区填满或程序退出才落盘。
- 重定向后务必在关键位置调用 System.out.flush(),尤其在程序异常退出前
- 避免依赖 println() 的自动刷新——在非终端环境下它不一定生效
- 检查重定向目标 OutputStream 是否抛异常(PrintStream 默认吞掉 IOException,需调用 checkError() 主动发现)
多线程中输出乱序或丢失
System.out 是线程安全的(内部加锁),所以不会崩溃或数据损坏,但它不保证输出顺序。多个线程同时调用 print() + flush(),内容可能交错;如果某个线程用 print() 但忘了 flush(),它的输出就可能被“拖到最后”才出现,甚至因 JVM 退出而丢失。
- 每个异步任务中,若使用 print(),后面必须紧跟 System.out.flush()
- 不要靠输出顺序判断执行顺序——这是缓冲和调度共同作用的结果
- 真正需要结构化、可重定向、线程友好的日志,请用 Log4j、SLF4J 或 Logback,而非 System.out
flush() 和 close() 别混用
flush() 只清空缓冲区,流保持可用;close() 会先 flush 再释放资源,之后再调用 println() 就会失败。System.out 是 JVM 全局资源,正常情况下永远不要调用 System.out.close()。
- System.out.flush() → 安全,随时可用,适合确保实时性
- System.out.close() → 风险操作,关闭后所有输出失效,仅在极特殊封装场景下谨慎使用











