StringBuffer线程安全而StringBuilder不安全:前者所有public方法均含synchronized修饰符,后者完全无;多线程并发修改时前者数据一致,后者易出错;单线程下StringBuilder性能高5–15倍;二者toString缓存机制差异也印证了设计目标不同。

看方法签名里有没有 synchronized
最直接的判断方式:打开 JDK 源码或 IDE 的跳转,查 append()、insert()、delete() 这些公共方法的声明。StringBuffer 的每个方法都带 synchronized 修饰符,StringBuilder 完全没有。
比如 StringBuffer.append(String) 是 public synchronized StringBuffer append(String str);而 StringBuilder.append(String) 就是 public StringBuilder append(String str)。这个差异不是“可能有”,而是所有公开方法都严格遵循——没例外。
多线程同时写同一个实例时会不会出错
这是检验线程安全最真实的场景。用两个线程反复对同一个对象调用 append("x") 10000 次,StringBuffer 总能输出 20000 个 x;StringBuilder 很大概率少于 20000 个,甚至抛 ArrayIndexOutOfBoundsException 或产生乱序、截断等不可预测结果。
- StringBuffer 因为锁住整个方法,第二线程必须等第一线程执行完才进,数据始终一致
- StringBuilder 没锁,两个线程可能同时读取当前
count值、同时计算新位置、同时写入 char 数组,导致一次写被另一次覆盖
性能差距在单线程下是否明显
是,而且非常显著。同步锁的获取/释放、内存屏障、上下文切换这些开销在单线程里纯属冗余。实测中,相同拼接逻辑下 StringBuilder 通常比 StringBuffer 快 5–15 倍(取决于字符串长度和操作频次)。
这意味着:如果你确认只在一个线程里用(比如 Web 请求处理中的局部变量),选 StringBuilder 不仅正确,而且必要;反过来,若误把 StringBuilder 当 StringBuffer 用在 Servlet 的成员变量上,就等于埋了并发隐患。
toString() 缓存机制也暴露了设计意图
StringBuffer 内部有个 toStringCache 字段,每次修改前都会设为 null,而 toString 时会先检查缓存再决定是否复制数组;StringBuilder 根本没这字段。
这个细节说明:StringBuffer 在意“多次 toString 调用”的效率权衡,愿意多占一点内存、多维护一个状态来减少重复拷贝——前提是它得保证多线程下调用 toString() 时看到的数据是某次完整修改后的结果,而不是中间态。这种权衡本身,就是为线程安全服务的。
length() 再 charAt())的业务逻辑安全。这类复合操作仍需外部加锁或改用更高层抽象。

















