直接用StringBuffer替换StringBuilder可保证线程安全,因其所有修改方法均加synchronized锁,确保共享实例的原子操作;但需注意共享方式、调用边界及避免重复加锁。

直接用 StringBuffer 替换 StringBuilder 就能保证线程安全——前提是多个线程共享同一个 StringBuffer 实例,且所有修改操作都通过它的同步方法(如 append、insert、delete)进行。
为什么换就能安全
StringBuffer 的每个修改方法内部都加了 synchronized 锁,比如:
-
append(String)是同步方法,同一时刻只允许一个线程执行 - 底层共用的
char[]数组不会被并发写入破坏 - 不会出现字符丢失、错位、数组越界等未定义行为
而 StringBuilder 没有任何同步机制,多线程共用时结果不可预测——不是偶尔出错,而是必然出错,只是表现形式可能随机(比如长度不对、内容混杂、甚至抛异常)。
怎么正确替换使用
不是简单把 new StringBuilder() 改成 new StringBuffer() 就完事,关键在“共享方式”和“调用边界”:
立即学习“Java免费学习笔记(深入)”;
- 声明为 static 或成员变量,让多个线程能访问同一个实例(例如日志缓冲区)
- 所有字符串拼接操作必须走 StringBuffer 自带方法,不要拆成
length() + append()这类非原子组合 - 避免在外部再套
synchronized块——重复加锁反而降低性能,还可能引发死锁 - 初始化时指定合理容量(如
new StringBuffer(2048)),减少扩容时的锁竞争
哪些情况不能靠换就解决
单纯替换类名不等于万无一失,以下场景仍需额外设计:
- 如果各线程只是各自拼一段,最后合并——更适合用 ThreadLocal
,比共用 StringBuffer 更高效 - 如果拼接频率极高(如每毫秒数百次),单点锁会成为瓶颈,应改用分段收集 + 最终 join
- 如果只是读取最终结果(
toString()),无需额外同步;但若拼接后还要反复解析或截取,建议拼完再统一处理
对比 StringBuilder 的典型风险示例
下面这段代码在多线程下极大概率出错:
❌ 错误写法(共用 StringBuilder)StringBuilder sb = new StringBuilder();Thread t1 = new Thread(() -> sb.append("A")); Thread t2 = new Thread(() -> sb.append("B"));
运行结果可能是 "AB"、"BA"、"A"、"B",甚至抛 ArrayIndexOutOfBoundsException。
换成 StringBuffer 后,结果一定是 "AB" 或 "BA"(顺序不确定,但内容完整无损)——这就是线程安全的实质:不保证执行顺序,但保证每次操作的原子性和数据完整性。


















