StringBuffer 的线程安全机制,实质上是在所有修改方法上添加了 synchronized 锁——例如 append、insert、delete 等操作,均受同一把 this 锁保护。同一时刻只允许一个线程对内部的 char[] 数组和 count 字段进行修改,从而保障数据一致性。但代价显而易见:其性能低于无锁的 StringBuilder。

一句话总结:StringBuffer 是线程安全的,适合多线程环境下的字符串拼接。 它通过在全部修改方法上添加 synchronized 来确保并发安全,但性能开销大于非同步的 StringBuilder。
为什么 StringBuffer 能在多线程中安全使用
其关键设计并不复杂:每个可能改变内部状态的方法均被声明为 synchronized,即同一时刻只允许一个线程进入操作。例如:
append(String)整体加锁,多个线程调用时会依次排队;- 内部字符数组
char[] value的读写不会出现脏读、丢失更新等问题; - 扩容逻辑(如
ensureCapacity)也受同步保护,避免多个线程同时触发数组复制导致数据错乱。
典型多线程使用方式
最常见的方式是将 StringBuffer 实例作为共享对象,多个线程直接调用 append(),无需额外加锁。实践中常见于日志聚合、配置拼接、批量构建文本等场景。但需注意:操作是线程安全的,但最终拼接结果的业务语义仍需自行把控——例如拼接顺序是否重要,锁无法保证这一点。
与 StringBuilder 的对比选择
选择策略其实很清晰:
- 明确只在单线程中使用 → 优先选择
StringBuilder,功能相同且无同步开销,性能更优; - 多线程且需要共享拼接结果 → 使用
StringBuffer; - 多线程但每个线程各自拼接、不共享实例 → 单个线程内使用
StringBuilder更高效; - 对性能敏感且需要更高并发能力,可考虑采用
ThreadLocal来避免锁竞争。
实际编码注意事项
尽管线程安全,但一些细节会影响正确性和效率:
- 不要在同步块外缓存或暴露内部数组(例如调用
toString().toCharArray()后再修改该数组); - 避免在循环中频繁创建新的
StringBuffer实例,复用更为明智; - 如果只需拼接固定数量的字符串,使用
String.join()或String.concat()可能更简洁; - 高并发下大量拼接,可评估是否改用
ConcurrentLinkedQueue加合并策略,而非局限于单个同步对象。
