StringBuffer线程安全靠所有public修改方法(如append、insert)加synchronized修饰符锁住this实例;它保障原子性、可见性、有序性,但多方法组合操作不具原子性。

Java 面试中问到 StringBuffer 线程安全的底层实现,核心答案就一句话:它靠每个公开的修改方法(如 append、insert、delete、reverse)都加上 synchronized 修饰符,锁住当前实例(this),从而保证同一时刻只有一个线程能执行这些操作。
同步落在每个 public 方法上
翻开 JDK 源码(以 append 为例):
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
关键点很明确:
立即学习“Java免费学习笔记(深入)”;
- 所有会改变内部状态的 public 方法都带 synchronized,包括 length()、capacity() 这类读方法其实也加了(但它们不修改数据,加锁主要是为可见性)
- 锁对象是 StringBuffer 实例本身,即 this,不是类锁(static synchronized)
- 真正修改 char[] value 和 count 的逻辑,由父类 AbstractStringBuilder 完成,但入口被 synchronized 包裹,确保临界区受控
为什么用 this 锁而不是类锁
这是设计上的关键取舍,目的是最小粒度保护共享状态:
- 两个不同的 StringBuffer 对象(比如 sb1 和 sb2)可以被多个线程同时操作,互不阻塞
- 只有多个线程操作同一个 StringBuffer 实例时,才会因争抢 this 锁而排队
- 避免全局串行化,兼顾安全性与并发效率
synchronized 带来的三重保障
它不只是“让线程排队”,更在 JVM 层面提供内存模型语义:
- 原子性:像 count++、数组扩容、字符复制等复合操作,在 synchronized 块内不可分割
- 可见性:线程进入同步块前强制从主内存读取 value[] 和 count;退出时把修改刷回主内存,其他线程立即可见
- 有序性:禁止指令重排序,且建立 happens-before 关系,确保多线程下操作顺序可预期
注意它的能力边界
面试官常会追问“它真的万无一失吗?”,答案是否定的:
- 单个方法(如 append)是线程安全的,但多个方法组合不是原子的。例如:
if (sb.length() == 0) sb.append("x");中,length() 是非同步读方法,中间可能被其他线程修改,导致竞态 - toString() 返回的是新 String,后续对这个字符串的操作与 StringBuffer 锁无关
- 若需多步逻辑原子性(比如先查长度再删一半),必须手动加 synchronized(sb) 块包裹整个操作


















