stringbuffer线程安全但性能差,因所有方法加synchronized锁整个对象,导致高并发下锁竞争激烈、扩容时双重同步开销大;适用场景仅限低并发、无法重构为线程私有拼接的共享读写。

StringBuffer 在多线程环境下确实线程安全,但它的同步机制本身就是性能瓶颈的根源——不是它“不能用”,而是用法和场景稍有偏差,就容易拖慢整体吞吐。
同步锁粒度粗,是核心瓶颈
StringBuffer 的所有 public 方法(如 append()、insert()、toString())都加了 synchronized 关键字,锁的是整个对象实例。这意味着:
- 即使多个线程操作的是完全不重叠的字符位置,也必须排队等待同一把锁
- 高并发下锁竞争激烈,大量线程陷入阻塞或自旋,CPU 时间花在上下文切换而非实际拼接上
- 方法链式调用(如 sb.append("a").append("b").append("c"))会触发多次同步进出,开销叠加
扩容时的双重同步开销
当内部 char[] 数组容量不足时,StringBuffer 会执行扩容(默认策略:新容量 = 旧容量 × 2 + 2)。这个过程包含两个同步步骤:
- 先同步检查当前容量是否足够
- 再同步执行数组复制与替换
若多个线程几乎同时触发扩容(比如批量日志写入),不仅争抢锁,还可能重复执行冗余的数组拷贝,进一步放大延迟。
与 StringBuilder 和 ConcurrentHashMap 的对比落差
在 10 万次字符串拼接测试中:
- StringBuffer 平均耗时约 30 毫秒
- StringBuilder(单线程)仅需 15 毫秒
- 若改用线程局部拼接 + 合并(如 ThreadLocal
),总耗时可压至 18–22 毫秒,且无锁竞争
更关键的是:现代高并发场景往往不需要“全程共享一个 StringBuffer 实例”。例如日志聚合、SQL 拼装、HTTP 响应组装等,更适合按线程/请求隔离处理,最后再合并结果。
真正需要 StringBuffer 的典型场景
它并非过时,而是适用面较窄。符合以下全部条件时才建议直接使用:
- 共享实例被多个线程频繁读写(非只读)
- 无法重构为线程私有 + 后续合并模式
- 并发度不高(比如后台定时任务中几个线程协作构建配置文本)
- 代码维护优先级高于几毫秒级性能损耗
其余情况,优先考虑 ThreadLocal
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











