StringBuffer 通过方法级 synchronized 锁保障线程安全,确保单次操作原子性,但组合操作需手动同步,性能约为 StringBuilder 的 1/2.8,适用于多线程共享实例场景。

StringBuffer 对多线程写操作的保护,靠的是每个公开修改方法(如 append、insert、delete、reverse)都加了 synchronized 关键字,锁住当前实例对象(this),确保同一时间只有一个线程能执行这些操作。
方法级同步锁保障单次操作原子性
每次调用 append() 或 delete() 这类方法时,线程必须先获取 StringBuffer 实例自身的 monitor 锁;没拿到锁就阻塞等待。一旦进入方法体,整个操作——包括更新内部 char[] 数组、修改 count 字段、处理扩容逻辑——都在一个同步块内完成,不会被其他线程打断。这就避免了“读到一半被覆盖”或“长度与内容不匹配”这类中间状态错乱。
- 底层复用 AbstractStringBuilder 的可变 char[] 和 count 字段,所有字段访问都被包裹在 synchronized 作用域中
- 像 count += len 这样的复合赋值,不会被拆成“读-改-写”三步暴露给并发,天然具备原子性
- toString() 方法也同步,且内部维护 toStringCache,重复调用不重复生成字符串对象
不是所有多线程场景都需要它
它只在多个线程共享同一个 StringBuffer 实例并并发读写时才真正起作用。比如 static 字段、被多个 Runnable 共同引用的成员变量。如果每个线程都 new 自己的 StringBuffer,那完全没必要用它——此时 StringBuilder 性能通常高 2–3 倍。
- 常见误用:在 run() 方法里 new StringBuffer() 后仅本线程使用,却仍选 StringBuffer
- 反模式:把 StringBuffer 当作“线程安全万能解”,忽略 ThreadLocal<StringBuilder> 或并行流 + 不可变字符串等更高效替代方案
组合操作仍需手动同步
单个方法是线程安全的,但多个方法连用就不自动安全。例如 “先判断长度再追加” 是典型竞态:
立即学习“Java免费学习笔记(深入)”;
- 错误写法:if (sb.length() < 10) sb.append("x"); —— length() 和 append() 是两个独立同步方法,中间可能被其他线程修改 sb
- 正确做法:synchronized (sb) { if (sb.length() < 10) sb.append("x"); } —— 用显式同步块包裹整个逻辑
- 类似情况还包括:检查是否为空后再 clear()、根据 substring 结果决定是否 insert() 等
性能代价明确但可控
同步带来确定性,也带来开销:实测 4 线程并发拼接各 10 万次,StringBuffer 耗时约为单线程 StringBuilder 的 2.8 倍;锁竞争随线程数增加而加剧。但它不是“慢得不能用”,而是“稳得有代价”。
- 合理预分配容量(如 new StringBuffer(128))可减少扩容次数,间接降低锁争抢频率
- 避免在高频循环内反复调用 toString(),利用其缓存机制提升效率
- 若写操作远多于读操作,且无强一致性要求,可考虑更轻量的并发策略(如分段聚合)


















