stringbuffer线程安全真实有效,通过所有公开修改方法加synchronized修饰、以this为锁对象实现粗粒度同步,确保多线程操作不出现数据错乱,但同步开销导致单线程性能比stringbuilder低50%–85%。

StringBuffer 的线程安全是真实有效的,但代价明确——它用方法级 synchronized 锁住整个实例,确保多线程并发调用 append、insert、delete 等操作不会出现数据错乱;可这个“安全”会拖慢性能,尤其在单线程或高吞吐场景下,同步开销不可忽视。
线程安全是怎么实现的
StringBuffer 所有公开修改方法(如 append()、insert()、reverse())都加了 synchronized 修饰符,锁对象就是 this 实例本身。这意味着:同一时刻只能有一个线程执行这些方法,其他线程必须排队等待。这种粗粒度锁能防止竞态条件,比如两个线程同时往同一个 StringBuffer 里追加字符导致内容重叠或丢失。
- 锁粒度大:每次调用都锁定整个对象,哪怕只是读 length() 或写一小段字符
- 无锁升级机制:不像 ConcurrentHashMap 那样分段加锁,也没有 CAS 优化
- toString() 有缓存(toStringCache),但缓存更新仍受同步保护,不额外提升并发效率
性能损失到底有多大
实测数据稳定显示:在纯单线程场景下,StringBuffer 比 StringBuilder 慢约 50%–85%。例如 100 万次 append 操作,JDK 17 环境典型耗时为 StringBuffer 120ms vs StringBuilder 65ms;若循环中反复创建新实例(非复用),差距缩小但仍存在 10% 左右开销。
- 同步指令带来 JVM 层面的 monitor enter/exit 开销
- 频繁上下文切换(即使单线程)也会轻微影响 CPU 流水线效率
- 现代 JIT 虽能部分消除无竞争同步,但无法完全抹平设计层面的锁成本
什么情况下必须用 StringBuffer
只有当多个线程共享同一个 StringBuffer 实例,并且无法通过代码隔离(如 ThreadLocal、方法内局部变量)避免竞争时,才需要它的线程安全保证。典型场景包括:
- 全局日志缓冲器(多线程共用一个 sb 收集日志后批量刷盘)
- Servlet 容器中跨请求共享的配置构建器(极少见,通常应避免)
- 遗留系统中无法重构的并发字符串拼接逻辑
注意:如果每个线程都 new 自己的 StringBuffer,那和 StringBuilder 效果一样,还多背一份同步包袱。
更合理的替代方案
绝大多数所谓“多线程需求”,其实可通过设计规避对 StringBuffer 的依赖:
- 优先用 StringBuilder + 局部变量:95% 以上字符串拼接发生在方法内部,天然线程隔离
- 需要跨线程传递结果时,拼完再 toString() 传 String,而不是传可变对象
- 真需并发构建,考虑无锁方案:如用 AtomicInteger 控制索引 + char[] 手动填充,或用 ConcurrentLinkedQueue 收集片段再合并
- 日志类场景可用 Log4j2 的 AsyncLogger,底层已做高性能缓冲,无需自己操心 StringBuffer
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











