
StringBuffer 在高并发场景中不是性能优先选择,而是线程安全的折中方案。
为什么 StringBuffer 不适合高并发拼接
StringBuffer 所有 public 修改方法(如 append、insert、delete)都加了 synchronized 关键字,意味着每次调用都要竞争同一把对象锁。在 QPS 过万的系统里,这会迅速成为锁瓶颈——线程排队等待、CPU 花在上下文切换和锁竞争上的时间远超实际拼接开销。
实测数据(JDK 21,10 万次循环拼接):
- String 拼接(+=):约 4568ms
- StringBuilder:约 12ms
- StringBuffer:约 28ms
看似只慢一倍,但这是单线程基准;一旦多线程争抢同一 StringBuffer 实例,耗时会呈非线性增长,甚至出现线程阻塞堆积。
真正高并发下的替代方案
直接共享一个 StringBuffer 实例并行操作,是典型的反模式。更合理的做法是:
- 无状态拼接场景(如日志格式化、JSON 字段生成):每个线程用独立的 StringBuilder,避免共享。Spring Boot 默认日志框架(Logback)正是这样处理 MDC 和参数拼接的
-
需要跨线程聚合结果(如批处理汇总):改用线程安全容器,比如 ConcurrentLinkedQueue
收集各线程结果,最后用单个 StringBuilder 合并 - JDK 14+ 可选:使用 ConcurrentStringBuilder(非标准 API,部分厂商 JDK 提供),或封装带分段锁的自定义缓冲区
StringBuffer 的合理使用边界
它仍有明确价值,但仅限于以下情况:
- 低频、偶发、且必须复用同一实例的多线程场景(例如配置类中的全局格式化器,每分钟调用几次)
- 遗留代码迁移成本过高,又无法重构为不可变或局部变量模式
- 作为教学示例理解 synchronized 基础机制,而非生产首选
电商订单系统(QPS 10 万)的真实优化路径是:把日志拼接从 StringBuffer 切换为 ThreadLocal
一句话决策建议
高并发字符串拼接,优先用 StringBuilder + 局部变量或 ThreadLocal;需要线程安全聚合,用无锁队列 + 单线程合并;只有当“共享可变状态”本身是设计必需,且并发度极低时,才考虑 StringBuffer。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











