StringBuffer 的线程安全靠每个 public 修改方法加 synchronized 锁住 this 实例,保障单次调用原子性,但多步操作需手动同步,性能低于 StringBuilder。

Java 中 StringBuffer 的线程安全,靠的是每个公开修改方法都加了 synchronized,锁住的是实例对象本身(this),不是类、也不是静态资源。它不靠额外工具或包装,而是从 JDK 1.0 就定下的原生设计——简单直接,但有明确边界。
同步落在每个方法上,不是整个类
StringBuffer 继承自 AbstractStringBuilder,所有关键操作(append、insert、delete、reverse、setCharAt 等)都被声明为 public synchronized。比如:
@Override<br>public synchronized StringBuffer append(String str) {<br> toStringCache = null;<br> super.append(str);<br> return this;<br>}
这意味着:每次调用时,JVM 会检查该 StringBuffer 实例的 monitor 锁;若已被占用,当前线程就等待;执行完自动释放。字节码里对应 ACC_SYNCHRONIZED 标志,由 JVM 底层支持,无需手动写 synchronized 块。
立即学习“Java免费学习笔记(深入)”;
锁对象是 this,不同实例互不干扰
它用的是对象级锁,不是类锁(static synchronized)。所以:
- 两个独立的 StringBuffer 对象(如
sb1和sb2)可以被不同线程同时操作,完全不阻塞 - 只有多个线程操作同一个 StringBuffer 实例时,才会排队串行执行
- 这种设计符合“最小粒度保护共享状态”的原则,既安全又不过度限制并发
只保单个方法原子性,多步操作仍需手动同步
synchronized 方法保障的是单次调用的原子性——比如一次 append() 不会中途被打断、不会出现数组越界或长度错乱。但它不管逻辑组合:
-
if (sb.length() < 10) sb.append("x");——length()是非同步方法,append()是同步方法,中间窗口可能被其他线程改写 -
int len = sb.length(); sb.delete(0, len / 2);—— 第二步执行时,len可能已失效 -
String s = sb.toString(); s.toUpperCase();——toString()返回新字符串,后续操作与sb锁无关
这类场景必须显式加锁:synchronized(sb) { ... },把多步操作包进同一临界区。
性能代价清晰,别滥用
每次调用都锁整个实例,属于粗粒度同步:
- 即使只是读
length()或追加几个字符,也要竞争锁 - 单线程下比 StringBuilder 慢 50%–85%,实测 100 万次
append操作差距明显 - 没有分段锁、CAS 或锁优化机制,纯靠 monitor 竞争
如果只是局部变量、单线程使用,或每个线程独占一个 StringBuffer 实例,那完全没必要用它——优先选 StringBuilder 或 ThreadLocal 包装。


















