StringBuffer线程安全依赖方法级synchronized锁住实例对象,确保单个操作原子性,但多步操作需显式同步;仅当多线程共享并并发读写同一实例时才真正必要,否则应优先选用StringBuilder或ThreadLocal优化性能。

Java 中 StringBuffer 的线程安全,靠的是方法级 synchronized 锁住整个实例对象,确保同一时刻只有一个线程能执行它的修改操作。但这不等于“所有场景都自动安全”,关键得看怎么用。
同步机制怎么起作用
StringBuffer 的 append、insert、delete、reverse、toString 等公开方法,全部声明为 synchronized。这意味着:
- 每次调用这些方法时,都会尝试获取该 StringBuffer 实例自身的对象锁(
this) - 若锁已被其他线程持有,当前线程会阻塞等待,直到锁释放
- 单个方法内部的操作(比如一次 append)是原子的,不会出现字符数组越界、长度错乱或内容覆盖
单方法安全 ≠ 多步操作安全
方法本身同步,不代表跨方法的逻辑自动受保护。常见陷阱是“先检查、再操作”:
- 错误写法:
if (sb.length() < 10) sb.append("x");—— length() 和 append() 是两个独立同步方法,中间可能被其他线程修改 sb - 正确做法:用显式同步块包裹整个判断+操作逻辑:
synchronized (sb) {<br> if (sb.length() < 10) {<br> sb.append("x");<br> }<br>}
什么时候真该用 StringBuffer
不是“多线程”就一定需要 StringBuffer。它只在以下情况真正必要:
立即学习“Java免费学习笔记(深入)”;
- 多个线程**共享同一个 StringBuffer 实例**(如作为类的 static 字段、或被多个 Runnable 共同引用)
- 多个线程**并发读写**该实例(不只是各自拼接后丢弃)
- 遗留系统依赖其同步语义,且重构成本过高
反例:在 for 循环或 run() 方法里每次 new StringBuffer,仅本线程使用——这时用 StringBuilder 更快,性能通常高 2–3 倍。
性能代价与更优替代方案
每个 synchronized 方法都有锁竞争开销,在高并发写入场景下明显拖慢吞吐量。可考虑:
- ThreadLocal<StringBuilder>:每个线程独享 StringBuilder 实例,零锁、高性能
-
不可变 + 并发收集:用 ConcurrentLinkedQueue
收集片段,最后由单一线程合并 - 避免共享:设计上让字符串拼接局限于单线程上下文,从根本上消除同步需求

















