StringBuffer线程安全但性能较低,因其所有public方法均加synchronized锁,导致单线程下仍有monitor操作、内存屏障、锁状态检查等开销,且锁粒度粗、toString缓存需同步维护。

StringBuffer 确实线程安全,但这份安全不是免费的——它靠每个 public 方法加 synchronized 实现,而锁本身就有实实在在的运行时开销。
锁机制在 JVM 层的真实成本
synchronized 不是“开关”,而是完整的一套同步协议。哪怕单线程调用 StringBuffer,JVM 仍必须执行:
- 获取和释放对象监视器(monitor),触发内存屏障以保证可见性
- 检查锁状态(偏向锁→轻量级锁→重量级锁的可能升级路径)
- 每次进入/退出同步方法都插入额外字节码指令(ACC_SYNCHRONIZED 标志)
- toString() 中还要维护缓存(toStringCache)的一致性,进一步增加同步负担
粗粒度锁放大争用影响
StringBuffer 锁的是整个实例,不是某段字符或某个操作。这意味着:
- 两个线程同时调用
append("a")和length(),也得排队——读操作也被阻塞 - 连续链式调用如
sb.append("x").append("y").append("z")会三次进出锁,开销叠加 - 无法对内部 char[] 做分段加锁,哪怕逻辑上互不干扰,也无法并行
性能差距在典型场景中明显
实测数据表明,开销不是理论上的:“低”或“轻微”,而是可量化的实际损耗:
立即学习“Java免费学习笔记(深入)”;
- 单线程 5 万次拼接:StringBuilder 耗时约 16ms,StringBuffer 约 21ms(慢 30%)
- 10 线程并发 append 同一实例:StringBuffer 吞吐下降超 30%,99 分位延迟升至 124ms
- 高频 toString() + 清空循环:锁持续持有 + 缓存管理拖慢整体节奏,StringBuilder 直接复制反而更稳
真正该用 StringBuffer 的时候
不是“多线程就默认选它”,而是满足两个前提:
- 多个线程确实共享同一个 StringBuffer 实例,并且都做写操作
- 无法重构为线程隔离方案(如 ThreadLocal
)或不可变设计
否则,绝大多数情况——方法内局部变量、单线程批量处理、高吞吐服务逻辑——用 StringBuilder 更合理,也更符合现代 Java 实践。


















