单线程用 stringbuilder,多线程共享修改同一实例才用 stringbuffer;二者底层数组和扩容策略相同,差异仅在 synchronized 修饰,stringbuilder 无锁故快 10%–15%。

单线程环境下,StringBuilder 明显快于 StringBuffer;多线程并发修改同一字符串缓冲区时,才需要 StringBuffer 的同步保障。
性能差异的核心原因
两者底层实现几乎完全一致:都基于可变的 byte[](JDK 9+)或 char[] 数组,扩容策略相同(容量不足时扩为 原容量 × 2 + 2),append、insert 等操作逻辑也一样。
关键区别只在方法是否加 synchronized:
- StringBuffer 的 append、toString、length 等关键方法都带 synchronized 修饰符
- StringBuilder 所有方法均无同步控制
- 这意味着在单线程中,StringBuffer 多出锁获取/释放的开销,实测慢 10%–15%
什么时候该选 StringBuffer
不是“多线程环境”就一定用 StringBuffer,而是要看是否多个线程会同时调用同一个 StringBuffer 实例的修改方法。
- 局部变量场景(如方法内 new 出来、仅本线程使用)→ 用 StringBuilder 就够了
- 作为类成员被多个线程共享且频繁调用 append() → 才需 StringBuffer
- 现代开发中,这类共享可变缓冲区的设计已较少见;更多是用不可变对象 + 线程局部变量(ThreadLocal)或重构为无状态逻辑
实际写法建议
避免凭感觉选,按明确规则来:
- 循环拼接(尤其 10 次以上)→ 必用 StringBuilder,禁用 s += "x"
- 构造时若能预估最终长度(如拼接 500 个平均 10 字符的字符串)→ 写 new StringBuilder(5000),减少扩容次数
- 日志拼接、JSON 组装、SQL 构建等典型单线程高频场景 → 默认 StringBuilder
- 只有经代码审查确认存在跨线程共享并修改同一实例的情况 → 才替换为 StringBuffer
编译器优化的边界要清楚
Java 编译器(从 JDK 5 起)会对非循环、单行的 + 拼接自动转成 StringBuilder,比如:
String s = "a" + "b" + "c"; // 编译后就是常量,无运行时开销
String s = str1 + str2 + str3; // 编译为 new StringBuilder().append(...).toString()
但一旦进入 for/while 循环,这种优化就失效了,每次迭代仍会新建 StringBuilder → 必须手动改写。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











