优先用 stringbuilder,仅当真需多线程共享同一实例时才用 stringbuffer;其同步机制是兜底而非增强,性能损耗显著(jdk17+下慢85%),多数场景可通过 threadlocal 或无锁设计规避。

直接看线程是否共享同一个实例——不共享,就用 StringBuilder;真共享且无法重构,才考虑 StringBuffer。它的线程安全不是“增强功能”,而是兜底机制,代价是性能明显下降。
先确认:你真的需要 StringBuffer 的线程安全吗?
多数项目里,所谓“多线程需求”其实并不存在。只要 StringBuffer 是方法内局部变量、或每个线程 new 自己的实例,那它的 synchronized 就纯属冗余开销,和 StringBuilder 效果一样,还更慢。
- Web 请求处理中的参数拼接、日志组装、SQL 构建——都是单线程上下文,用 StringBuilder 即可
- 线程池任务中各自拼接结果再汇总——各用各的 StringBuilder,最后用 String.join() 或 StringBuffer 合并
- 全局日志缓冲器、遗留系统配置拼接器等极少数场景,才真正需要多个线程往同一个 StringBuffer 实例里写
性能差距不是理论值,而是实测瓶颈
在 JDK 17+ 环境下,100 万次 append 操作:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- StringBuilder:约 65ms
- StringBuffer:约 120ms(慢 85%)
- 4 线程并发时,StringBuffer 吞吐量几乎不随线程数增长,大量时间花在排队抢锁上
不只是“慢一点”,而是锁粒度粗(整个方法级)、连 length() 这类读操作也同步、无 CAS 或分段锁优化——它为安全牺牲了全部并发弹性。
用对 StringBuffer 的关键细节
即使必须用,也要避开常见低效用法:
- 构造时指定合理初始容量,比如 new StringBuffer(1024),减少扩容时的锁争抢
- 不要在外层再套 synchronized 块,重复加锁只会放大开销
- 避免“先判断再修改”的竞态逻辑(如 if (buf.length()
- 拼接完成后尽早 toString(),后续只读 String——不可变对象天然线程安全,且无锁
比选类更重要的,是设计绕开共享
硬扛同步不是高并发的解法,解耦才是:
-
ThreadLocal
:每个线程独享实例,零锁、可复用,适合日志格式化、模板渲染等高频场景 - 各线程生成字符串片段 → 收集到 ConcurrentLinkedQueue 或 CopyOnWriteArrayList → 单线程合并
- 用 Log4j2/SLF4J 的异步 Appender 替代手写日志缓冲,底层已做线程隔离
- 极少量共享更新,改用 AtomicReference
+ CAS 替换,或用 StringBuilder 拼好再原子设值
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










