stringbuffer线程安全靠每个public修改方法加synchronized修饰,锁对象为this,确保count和value的更新原子可见;但多步操作如if-then-append需外层synchronized保护。

StringBuffer 的内部状态在并发操作下不会“错乱”,但它的变化是受锁保护的、逐方法原子的,不是业务逻辑连贯的——也就是说,单个 append 或 insert 能安全执行完,但 if-then-append 这类多步判断+修改就可能出错。
底层结构:char[] + count + 同步锁
StringBuffer 继承自 AbstractStringBuilder,核心字段只有三个:
- char[] value:真正存字符的数组,可动态扩容
- int count:当前有效字符长度(不是数组长度)
- 对象级 this 锁:所有 public 修改方法都 synchronized(this)
每次调用 append、insert、delete 等方法时,JVM 先获取该实例的 monitor 锁,再更新 value 和 count;toString() 也同步,还会维护一个 toStringCache(JDK 9+),但这个缓存本身也是被锁保护的,高并发下易失效,反而增加开销。
并发下状态变化的真实表现
多个线程同时调用同一 StringBuffer 实例的方法时,状态改变遵循以下规律:
- 任意时刻最多一个线程能进入 append(),其他线程阻塞等待——count 和 value 的修改是原子完成的
- 线程 A 执行 sb.append("a") 后,value 数组和 count 都已更新,且对其他线程可见(因 synchronized 的内存语义)
- 但若线程 A 先调用 sb.length() 得到 5,线程 B 紧接着 append("x") 把 count 改成 6,A 再调用 sb.append("y")——结果是 "....xy",而非预期的仅在长度不足时追加
- toString() 返回的是新 String 对象,后续对该 String 的任何操作(如 substring)不再受 StringBuffer 锁保护,也不影响其内部状态
扩容时的状态跃变
当 append 导致 char[] 不够用,StringBuffer 会触发扩容(通常是 oldCapacity * 2 + 2):
- 整个扩容过程(new char[]、System.arraycopy、赋值给 value、更新 count)都在 synchronized 块内完成
- 扩容期间其他线程完全阻塞,不会看到“半新半旧”的数组状态
- 但频繁扩容会加剧锁竞争——建议初始化时指定合理容量,如 new StringBuffer(1024)
注意:同步不等于业务一致
StringBuffer 保证的是每个方法调用的执行不交错,不是整个业务流程的线程安全。例如:
- 错误写法:
if (sb.length() == 0) sb.append("init");—— length() 和 append() 是两次独立加锁操作,中间可能被其他线程插入内容 - 正确做法:
synchronized(sb) { if (sb.length() == 0) sb.append("init"); }—— 显式扩大临界区,把判断和修改包进同一把锁
本质上,StringBuffer 的并发安全性是“方法级”的,不是“场景级”的。它兜得住数据结构不崩,兜不住你写的逻辑漏洞。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











