StringBuffer 线程安全而 StringBuilder 不安全,因前者所有公共方法加 synchronized,后者无同步;单线程下 StringBuilder 快 15%–25%,且 toString() 缓存优化实际无效。

StringBuffer 和 StringBuilder 的线程安全性差异在哪?
关键就一点:StringBuffer 的所有公共方法(如 append、insert、delete)都加了 synchronized,而 StringBuilder 完全没加。这意味着:
- 多个线程同时调用同一个
StringBuffer实例的append,不会出现数据错乱; - 同样操作
StringBuilder,极大概率产生脏数据(比如字符串拼接缺字、顺序混乱),且这种 bug 很难复现和调试。
这不是“理论风险”,而是 JVM 内存模型决定的:字符数组 value[] 的读写不满足原子性,没有同步机制时,一个线程看到的可能是另一个线程只写了一半的状态。
单线程下用 StringBuffer 会慢多少?
不是“慢一点”,而是有可测量的开销:
- 每次进入
synchronized方法,JVM 都要检查锁状态(偏向锁 → 轻量级锁 → 可能升级为重量级锁); - 即使是单线程,锁获取/释放本身就有 CPU 周期消耗;
- 实测在 JDK 17+ 环境下,10 万次
append操作,StringBuilder比StringBuffer快约 15%–25%,差距随操作复杂度增大。
所以别信“反正就我一个线程,用哪个都行”——只要没明确需要线程安全,StringBuffer 就是多花了钱还买了个锁。
立即学习“Java免费学习笔记(深入)”;
什么场景非得用 StringBuffer?
真实项目中极少,但必须用的典型情况只有:
- 全局共享的、被多个线程反复修改的字符串构建器(例如日志聚合器、动态 SQL 构造器),且你无法控制调用方线程模型;
- 遗留系统强制要求使用老 API(如某些 JDK 1.4 兼容库返回
StringBuffer); - 你在写一个会被别人多线程调用的工具类,且不希望使用者自己加锁。
注意:Spring、Logback、Jackson 等主流框架内部早已全部切换为 StringBuilder;如果你在写新代码还选 StringBuffer,大概率只是因为 IDE 自动补全弹出了它。
容易忽略的底层细节:toString() 缓存机制
StringBuffer 内部有个 toStringCache 字段,用于缓存上一次 toString() 的结果;每次 append 前都会把它置为 null。而 StringBuilder 没这层逻辑,每次 toString() 都直接调用 Arrays.copyOfRange(value, 0, count)。
这意味着:
- 如果你频繁调用
toString()且中间不做修改,StringBuffer有微弱优势; - 但现实中几乎没人这么用——字符串构建完通常只调一次
toString(),这个优化形同虚设; - 更重要的是,
toStringCache本身也是个对象引用,GC 压力略高,反而可能抵消那点好处。
真正该关心的,是别在循环里反复新建 StringBuffer 或 StringBuilder 实例——初始化容量(比如 new StringBuilder(1024))比纠结用哪个类影响大得多。










