直接用 StringBuilder 替代 StringBuffer 可提升 10%–15% 吞吐量,因其无锁设计避免了 synchronized 的开销、更易被 JIT 内联优化、减少内存屏障;但仅限真单线程场景,需确保实例不被多线程共享,并配合容量预设与合理 toString() 使用。

直接用 StringBuilder 替代 StringBuffer 就能提升 10%–15% 吞吐量,前提是确认场景为单线程且实例不被多线程共享。
为什么无锁能提速
StringBuffer 的 append、insert、toString 等方法全部加了 synchronized,每次调用都要进锁、检查、释放——哪怕只有一个线程在跑,JVM 仍要走完整套同步流程。StringBuilder 完全去掉这层开销,方法调用直落到底层 char[] 操作,没有竞争,也没有上下文切换成本。
- 高频拼接(如日志组装、JSON 构建)中,锁的累积延迟更明显
- 现代 JVM 对非同步方法的内联优化更激进,StringBuilder 更易被 JIT 编译器深度优化
- 避免了 synchronized 带来的内存屏障指令,减少 CPU 流水线停顿
确保“真单线程”才能放心去锁
不是代码没写多线程就等于安全,关键看 StringBuilder 实例的生命周期和作用域。
- ✅ 安全:Spring Controller 方法内 new 的 StringBuilder、MyBatis DAO 中局部拼 SQL、工具方法里作为参数传入并只在当前栈帧使用
- ❌ 危险:static 字段持有 StringBuffer/StringBuilder、CompletableFuture 异步任务间传递同一实例、ThreadLocal 未清理导致跨请求复用
- ⚠️ 注意:即使方法本身是单线程,若 StringBuilder 被塞进全局缓存或事件总线,就可能意外暴露给其他线程
配合容量预设,把性能拉满
无锁只是起点,扩容才是隐藏瓶颈。char[] 扩容需复制原数组,StringBuffer 在多线程下扩容还要抢锁;StringBuilder 虽无锁,但频繁扩容一样拖慢速度。
立即学习“Java免费学习笔记(深入)”;
- 根据业务典型长度预估容量,比如拼接用户信息通常不超过 512 字符 → new StringBuilder(512)
- 对固定模板拼接(如 "id={}&name={}&ts={}"),可按占位符数量 + 常量长度粗略估算
- 日志类高频场景建议设为 1024 或 2048,避免每条日志都触发扩容
别让 toString() 成为状态污染源
StringBuilder 的 toString() 返回的是新 String,但内部 char[] 会被缓存复用。如果之后继续 append,旧字符串内容可能被意外覆盖——尤其在日志、异步回调等场景中容易引发脏数据。
- 每次需要对外输出时,显式调用 toString() 获取快照,不要反复复用同一实例拼不同语义内容
- 避免在循环中反复清空(setLength(0))后重用,除非你完全掌控后续所有 append 逻辑
- 对关键结果(如返回给前端的 JSON 字符串),toString() 后立即丢弃 StringBuilder 实例,不复用


















