结论:单线程下StringBuilder明显快于StringBuffer,多线程下StringBuffer更安全但需权衡性能;String在循环拼接中性能最差,应避免。核心原因是StringBuffer所有方法加synchronized锁,引入锁获取、释放、内存屏障等开销,而StringBuilder无同步,复用相同底层实现,执行路径更短;实测10万次append,StringBuilder约12ms,StringBuffer约28ms,差距约2.3倍。

直接说结论:单线程下 StringBuilder 明显快于 StringBuffer,多线程下 StringBuffer 更安全但需权衡性能;String 在循环拼接中性能最差,应避免。
为什么 StringBuffer 比 StringBuilder 慢
核心原因在于同步开销。StringBuffer 的所有修改方法(如 append、insert、delete)都加了 synchronized 关键字,每次调用都会触发 JVM 的锁机制——即使当前只有单个线程在运行,也必须完成锁的获取与释放流程。而 StringBuilder 完全不加锁,底层复用同一套 AbstractStringBuilder 实现,仅省去同步逻辑,实际执行路径更短。
- 实测数据(JDK 17,10 万次 append):StringBuilder 约 12ms,StringBuffer 约 28ms,性能差距约 2.3 倍
- 锁竞争越少,差距越小;但只要存在同步块,就必然引入额外指令和内存屏障,无法绕过
- 扩容逻辑完全一致(旧容量 × 2 + 2),性能差异与内存分配无关,纯属并发控制成本
怎么写靠谱的性能测试
手写 System.currentTimeMillis() 测微秒级差异容易受 GC、JIT 预热、系统调度干扰,结果不可靠。推荐使用 JMH(Java Microbenchmark Harness):
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 启用预热(@Warmup)让 JIT 编译器充分优化代码
- 多轮测量(@Measurement)取平均值,排除瞬时抖动
- Fork 新进程隔离环境,避免前序测试污染后序
- 用 Blackhole.consume() 防止 JVM 优化掉无用计算
- 测试不同数据规模(如 100 / 1000 / 10000 次拼接),观察增长趋势而非单点值
多线程场景不能只看“线程安全”四个字
StringBuffer 的线程安全是方法级同步,粒度粗——整个 append 过程独占锁。若多个线程频繁拼接各自独立的字符串,反而会因争抢同一把锁造成串行化,吞吐不升反降。
立即学习“Java免费学习笔记(深入)”;
- 真正需要共享修改同一 StringBuffer 实例时,才体现其价值
- 更常见做法是:每个线程用本地 StringBuilder,最后汇总;或用 ThreadLocal<StringBuilder> 隔离实例
- JDK 14+ 提供了 ConcurrentStringBuilder(非标准 API,部分厂商扩展),但主流仍推荐组合方案而非依赖单一类
日常开发选哪个,看这三点
不用背理论,按实际场景判断:
- 拼接发生在循环里、日志构造、JSON 组装等单线程高频操作 → 选 StringBuilder
- 多个线程共用一个缓冲区,且必须实时协同修改(极少见)→ 才考虑 StringBuffer
- 只是赋值、传参、比较、少量固定拼接(如 "prefix" + value)→ String 完全够用,还更简洁


















