StringBuffer线程安全靠每个公开方法加synchronized实现,锁对象为this,虽保障多线程安全但带来明显性能开销;其价值仅在多线程共享同一实例且无法规避共享的场景下才真正体现。

StringBuffer 的线程安全不是免费的,它靠每个公开方法加 synchronized 实现,锁的是实例本身(this)。这种设计在多线程共享同一对象时能避免数据错乱,但同步开销会明显拖慢单线程或线程隔离场景下的性能。平衡的关键不在“要不要锁”,而在于“谁在用、怎么用、用在哪”。
线程安全的真实代价:不只是慢,更是阻塞
StringBuffer 的所有修改方法(append、insert、delete 等)都被 synchronized 修饰,意味着同一时刻只能有一个线程执行这些操作。这带来两个直接影响:
- 多线程高并发下,线程会排队等待锁,CPU 利用率下降,吞吐量受限
- 即使只是读操作(如
length()、charAt()),也因方法被同步而无法真正并发——这点常被忽略 - 锁粒度粗(整个方法级),一次 append 就锁住整个对象,哪怕只改末尾几个字符
什么场景下 StringBuffer 的线程安全真有用
它的线程安全性只有在满足以下全部条件时才真正发挥价值:
- 多个线程确实共享同一个 StringBuffer 实例(非局部变量、非 ThreadLocal)
- 这些线程会并发调用修改方法(不是只读)
- 业务逻辑无法通过重构规避共享(比如不能把拼接逻辑移到单线程内完成)
- 没有更轻量的替代方案(如用
ConcurrentHashMap缓存中间结果,或用CopyOnWriteArrayList拼接字符串列表)
典型例子:全局日志缓冲器、配置中心动态拼接模板、Servlet 容器中跨请求复用的格式化器——但这类场景在现代应用中已越来越少见。
立即学习“Java免费学习笔记(深入)”;
性能优化的务实路径:不硬扛,先绕开
与其纠结“怎么让 StringBuffer 更快”,不如优先考虑是否必须用它:
- 单线程循环拼接(如构建 SQL、JSON)→ 直接换 StringBuilder,性能通常提升 50%–100%
- 线程内多次拼接 → 把 StringBuilder 声明为局部变量,避免逃逸到堆上
- 需要预估长度 → 构造时指定足够容量(如
new StringBuffer(1024)),减少数组扩容拷贝 - 纯读多写少 → 考虑拼接完成后转成 String,后续只读 String(天然线程安全且无锁)
替代思路:比“选哪个类”更重要
当 StringBuffer 的同步成为瓶颈,升级方案往往不在字符串类本身:
- 用不可变 + 函数式:每次拼接生成新 String(适合短串、低频)
- 用
ThreadLocal<StringBuilder>:每个线程独享实例,零同步开销 - 用
java.util.StringJoiner:专为 join 场景优化,线程安全且语义清晰 - 异步聚合:把拼接任务提交到队列,由单线程消费合并,彻底消除竞争
本质上,StringBuffer 是 Java 1.0 时代对“多线程+可变字符串”的朴素解法。今天更值得花时间思考的,是架构层面如何让字符串拼接远离共享状态。



















