stringbuffer因所有public方法加synchronized,在单线程下仍需执行monitorenter/monitorexit、锁状态检查、内存屏障插入及monitor获取释放,锁粒度粗导致读写全阻塞,tostring缓存反增负担,而stringbuilder无锁开销更小。

因为 StringBuffer 的每个 public 方法都加了 synchronized,哪怕只在单线程里调用,JVM 也必须走完完整的锁流程——这不是“有没有竞争”的问题,而是设计层面就绕不开的开销。
同步机制本身就有执行成本
即使只有一个线程在跑,synchronized 仍会触发以下操作:
- 每次进入/退出方法都要执行
monitorenter和monitorexit字节码指令 - JVM 需检查锁状态(偏向锁 → 轻量级锁 → 重量级锁的判断路径)
- 插入内存屏障(memory barrier),保证字段可见性,影响 CPU 流水线效率
- 锁对象的监视器(monitor)获取与释放,涉及底层系统调用准备
锁粒度太粗,无法跳过排队逻辑
StringBuffer 把整个实例当作锁对象,所有方法(append、insert、length、toString 等)都同步。这就导致:
- 连续链式调用如
sb.append("a").append("b").append("c")会三次进出锁,开销叠加 - 哪怕只是读
length(),也要等前面的 append 完成——读写全被阻塞 - 无法对内部 char[] 做分段控制,逻辑上互不干扰的操作也强制串行
toString 缓存反而增加负担
StringBuffer 的 toString() 依赖 toStringCache 字段做缓存,但这个字段也被 synchronized 保护:
- 缓存更新要加锁,频繁调用时容易失效或置 null
- 而 StringBuilder 每次直接复制数组,省去了锁检查 + 缓存一致性维护的综合成本
- 在高频拼接+频繁转字符串的场景下,这个差异会快速累积
底层实现相同,差异只在锁
StringBuffer 和 StringBuilder 共享 AbstractStringBuilder,扩容策略、字符数组操作逻辑完全一致。唯一区别就是 synchronized 修饰符:
- StringBuilder 是普通 invokevirtual 调用,路径更短
- StringBuffer 多出 monitor 操作、状态维护、屏障插入等 JVM 层开销
- 实测 10 万次 append:StringBuilder 约 12ms,StringBuffer 约 28ms,差距达 2.3 倍
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











