单线程用stringbuilder,多线程共享同一实例时用stringbuffer;局部变量属单线程场景,类成员被多线程并发读写且未加锁才需stringbuffer;性能差异源于synchronized开销,预估容量比选择类型更关键。

直接看线程环境:单线程用 StringBuilder,多线程用 StringBuffer。这是最核心的判断依据,其他因素都围绕它展开。
先确认是否真处于多线程环境
不是“有多个线程”就等于“多线程共享操作同一对象”。关键看 StringBuffer 或 StringBuilder 实例是否被多个线程同时读写。
- 局部变量(方法内 new 出来)——即使在多线程任务中,每个线程都有自己的实例,属于单线程场景,选 StringBuilder
- 类成员变量 + 被多个线程调用的方法访问 + 未加锁保护 —— 存在并发修改风险,必须用 StringBuffer
- 用了 synchronized、ReentrantLock 或其他同步机制保护访问 —— 可改回 StringBuilder,避免双重同步开销
关注性能差异的实质来源
StringBuilder 比 StringBuffer 快,不是因为“算法更优”,而是因为去掉了所有方法上的 synchronized 关键字。这个差异在高频调用(如循环内 append 上万次)时会明显体现。
- 单线程下,10 万次 append,StringBuilder 通常比 StringBuffer 快 10%–20%
- 但若仅拼接几十次,两者耗时几乎无差别,不必过度优化
- 真正拖慢性能的往往是没预估容量——两者都支持构造时指定初始容量(如
new StringBuilder(4096)),避免多次数组扩容拷贝
别被“线程安全”误导成“绝对安全”
StringBuffer 的线程安全,只保证单个方法(如 append()、delete())是原子的。它不保证复合操作的原子性。
- 比如
if (sb.length() —— 这两步之间可能被其他线程插入操作,导致逻辑错误 - 这种场景仍需额外同步,此时用 StringBuffer 并没有带来实际优势,反而可能掩盖设计问题
- 与其依赖 StringBuffer 的同步粒度,不如明确划分共享边界,或改用不可变+线程局部变量等更清晰的并发模型
实际编码中的推荐动作
- 新建可变字符串对象时,默认写
new StringBuilder();只有明确需要跨线程共享且无法加锁时,才换为new StringBuffer() - IDE 通常会对 StringBuffer 在单线程场景下的使用给出警告(如 IntelliJ 的 “StringBuffer can be replaced with StringBuilder”)
- 团队代码规范中可约定:除非注释说明必要性,否则禁止直接使用 StringBuffer











