必须使用stringbuffer当且仅当多个线程共享并并发修改同一个可变字符串缓冲区对象,且无法通过加锁、threadlocal或不可变方式规避竞争;其他情况优先选用stringbuilder。

当多个线程**同时读写同一个字符串缓冲区对象**,且结果的正确性依赖于操作顺序时,必须使用 StringBuffer。
共享可变字符串对象被多线程并发修改
这是最核心的前提。如果几个线程共用同一个 StringBuffer 实例,并调用 append、insert、delete 等修改方法,而你又不能保证这些操作互斥(比如没加 synchronized 块或锁),那么数据就可能错乱。
- 例如:一个日志聚合器由多个工作线程向同一个 StringBuffer 写入日志片段,最后统一 flush 到文件——若用 StringBuilder,日志内容可能出现截断、重叠或字符错位
- 再如:Web 容器中多个请求线程共用一个用于拼接响应头的缓冲区,且未做同步控制
无法重构为局部变量或不可变传递
有些老系统或受限框架(如部分 Servlet 容器早期实现、某些中间件插件 API)要求复用某个全局或实例级的字符串缓冲对象,你无法把逻辑改成每个线程自己 new 一个 StringBuilder。
- 典型场景:自定义 Filter 或 Handler 中持有一个类成员 StringBuffer,被多个请求线程调用其方法
- 此时若强行换成 StringBuilder,就必须额外加锁或改造成 ThreadLocal,反而增加复杂度和出错风险
替代方案成本高于直接使用 StringBuffer
不是所有“多线程”都必须用 StringBuffer。只有当你评估后发现:加锁 + StringBuilder 的开销 > 直接用 StringBuffer 的同步开销,或者加锁逻辑易出错、难维护,才值得保留 StringBuffer。
- 比如:高吞吐但低并发(如每秒 100 次操作,仅 2–3 个线程争用),StringBuffer 的 synchronized 方法已足够轻量
- 再如:已有成熟模块大量使用 StringBuffer,且无性能瓶颈,强行替换引入回归风险得不偿失
注意:线程安全 ≠ 多线程环境就一定要用
很多场景下,所谓“多线程”只是指应用运行在多线程容器里,但实际字符串操作仍是线程隔离的:
- 每个请求线程内部拼 SQL 或 JSON —— 用 StringBuilder 更高效
- 使用 ThreadLocal
封装缓冲区 —— 避免竞争,性能更好 - 用不可变方式组装(如 Stream.collect(Collectors.joining()))—— 根本不涉及可变状态
真正“必须”用 StringBuffer 的情况其实不多,多数现代代码优先选 StringBuilder,只在明确存在共享可变状态且难以规避时才启用它。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











