StringBuffer 通过 synchronized 方法锁住 this 实现线程安全,保证单方法原子性但不保障多步操作原子性;不同实例可并发操作,同实例多线程需排队。

Java 中 StringBuffer 在处理共享字符串资源时,靠的是每个公开修改方法(如 append、insert、delete、reverse)自带的 synchronized 修饰符,锁住当前实例对象(this),从而保证单个方法调用的原子性。
同步机制落在方法级别
所有关键操作都加了 synchronized,意味着:
- 每次调用时,线程必须先获取该 StringBuffer 实例的对象锁;
- 若锁被占用,后续线程会阻塞等待,直到前一个线程执行完并释放锁;
- 底层字符数组扩容、count 更新、toStringCache 清理等动作,都在锁保护范围内;
- 非同步方法(如 length()、charAt())可并发读取,不参与锁竞争。
对象锁而非类锁,支持多实例并发
使用 this 锁而不是 static 锁,带来两个实际好处:
- 不同 StringBuffer 实例(比如 sb1 和 sb2)可以被多个线程同时操作,互不干扰;
- 只有操作同一个实例的多个线程才会排队,符合“最小粒度保护共享状态”的设计原则。
单方法安全 ≠ 多步操作安全
常见误区是以为 synchronized 方法能自动保护组合逻辑。例如:
立即学习“Java免费学习笔记(深入)”;
错误写法:
if (sb.length()
这里 length() 和 append() 是两次独立加锁调用,中间可能被其他线程修改 sb 状态,形成竞态条件。
正确做法:
用显式同步块包裹整个判断+操作:
synchronized (sb) {
if (sb.length() sb.append("x");
}
}
真正需要 StringBuffer 的典型场景
它只在以下情况才不可替代:
- 多个线程反复读写同一个 StringBuffer 实例(如作为 static 字段或跨 Runnable 共享);
- 日志聚合器中,多个工作线程向同一缓冲区追加日志行;
- 遗留系统依赖其同步语义,且重构成本过高。
反之,若每个线程都新建自己的 StringBuffer,或仅单线程内拼接后立即转为 String,则应优先选用 StringBuilder 或 ThreadLocal


















