stringbuffer的同步通过每个公开修改方法(如append、insert、delete、reverse)加synchronized修饰实现,锁对象为实例本身(this),确保同一时刻仅一个线程可执行其修改操作;非同步方法如length、charat可并发读取;采用实例锁而非类锁,使不同stringbuffer对象间操作互不阻塞,符合最小粒度线程安全原则。

StringBuffer 的同步不是靠外部加锁,而是每个关键方法内部自带 synchronized 修饰,本质是对象级锁(this 锁),确保同一时刻只有一个线程能执行修改操作。
同步是怎么实现的
StringBuffer 继承自 AbstractStringBuilder,但所有对外公开的修改方法(如 append()、insert()、delete()、reverse())都被声明为 public synchronized。这意味着:
- 每次调用这些方法时,线程必须先获取该 StringBuffer 实例的对象锁(即 this 锁);
- 若锁已被其他线程持有,当前线程会阻塞等待,直到锁被释放;
- 锁在方法执行完毕(包括异常退出)后自动释放,无需手动管理;
- 非同步方法(如
length()、capacity()、charAt())不参与锁竞争,可并发读取。
为什么用对象锁而不是类锁
使用实例对象锁(而非 static synchronized 类锁),是为了让不同 StringBuffer 实例之间互不影响:
- 两个独立的 StringBuffer 对象(如
sb1和sb2)可以被不同线程同时操作,不会互相阻塞; - 只有操作同一个 StringBuffer 实例的多个线程才会排队等待,符合“保护共享状态”的最小粒度原则;
- 这种设计在保证线程安全的同时,避免了过度串行化,比全局锁更合理。
哪些场景真正需要 StringBuffer
并不是所有多线程字符串操作都需要 StringBuffer。它适用的前提是:多个线程**共享并反复修改同一个 StringBuffer 实例**。典型场景包括:
- 日志聚合器中,多个工作线程向同一个缓冲区追加日志行;
- 旧版 Servlet 中,多个请求线程共用一个 StringBuffer 构建响应内容(现已少见);
- 某些遗留框架或工具类中,作为线程安全的中间拼接容器传递使用。
注意:如果每个线程都新建自己的 StringBuffer,那用 StringBuilder 更高效;如果只是临时拼接后立即转成 String,也无需考虑同步。
性能代价与替代思路
同步带来确定性,也带来开销:
- 高并发下,大量线程争抢同一把锁会导致上下文切换和等待延迟;
- 即使只读操作(如多次
toString())不加锁,但若紧邻写操作,仍可能因锁未释放而间接受阻; - 现代开发中,更推荐“无共享”设计:用 StringBuilder 在各线程内部分别构建,再通过线程安全容器(如
ConcurrentLinkedQueue)汇总,或用不可变对象 + 函数式组合规避共享状态。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











