stringbuffer线程安全仅保障单方法原子性,不保证多步逻辑安全;高并发下需严格控制共享范围、避免竞态、合理初始化容量,并优先考虑threadlocal或无锁方案替代。

StringBuffer 在高并发下“稳定”修改的前提,不是靠它自己扛住压力,而是靠你控制好共享范围和操作边界。它本身线程安全,但这种安全仅限于单个方法原子性——append、delete 等调用不会交错,不等于你的多步逻辑自动受保护。真要稳定,关键在“谁在用、怎么用、用在哪”。
只在真正需要长期共享的场景中使用
StringBuffer 的价值只在多个线程持续共用同一个实例并频繁修改时才成立。比如:
- 全局日志缓冲池:多个业务线程往同一个 buffer 写入日志片段,最后由刷盘线程统一 flush
- HTTP 响应头聚合器:多个 Filter 同时向同一 StringBuffer 添加 Header 字段
- 遗留系统中被多个 Service Bean 注入复用的字符串组装工具类
反例:Controller 方法里 new 出一个 StringBuffer,再传给几个子线程——此时每个线程该用自己的 StringBuilder,或用 ThreadLocal 包装。
避免“方法原子”误当“逻辑安全”
length() 和 append() 是两个独立同步方法,中间存在竞态窗口。以下写法危险:
❌ 错误
if (buf.length()
正确做法是显式同步整个判断+修改块:
✅ 正确
synchronized (buf) {
if (buf.length() buf.append("default");
}
}
注意:toString() 返回的是新 String 对象,后续对该 String 的任何操作都不再受 StringBuffer 锁保护;JDK 9+ 中 toStringCache 在高并发下调用易失效,反而增加开销。
性能瓶颈明确,别把它当高性能方案
StringBuffer 所有修改方法都 synchronized(this),锁粒度是整个实例。实测表明:
- 4 线程并发执行 10 万次 append,耗时约为单线程 StringBuilder 的 2.8 倍
- 线程数超过 4 后,吞吐量几乎不再增长,CPU 大量时间消耗在锁竞争和线程阻塞上
- 它不是“又安全又快”,而是“宁可慢一点,也要不错乱”
如果只是临时拼接(如日志格式化、SQL 构建),优先用 ThreadLocal
初始化容量能减缓扩容抖动
默认初始容量为 16,频繁扩容会触发数组复制,加剧锁竞争。若预估最终长度,建议显式指定:
- new StringBuffer(512) —— 适用于已知最大长度的日志行或响应体
- new StringBuffer("prefix") —— 构造时直接带内容,内部容量设为 content.length() + 16
但注意:容量预估只是优化点,不能替代对共享模型的审慎设计。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











