StringBuilder与StringBuffer区别详解
目录
核心结论
详细对比
深入解析
1. 线程安全 (Thread Safety)
2. 性能 (Performance)
3. 历史与版本
如何选择?
代码示例
单线程环境下的性能对比
多线程环境下的安全性
总结
核心结论
两者的核心区别在于:StringBuffer
是线程安全的,而 StringBuilder
是非线程安全的。正因为此,在单线程环境下,StringBuilder
的性能通常高于 StringBuffer
。
详细对比
特性 | StringBuffer | StringBuilder |
线程安全 | 是 (同步的,synchronized) | 否 (非同步的) |
性能 | 相对较低 (因为同步的开销) | 相对更高 (无同步开销) |
诞生版本 | JDK 1.0 | JDK 1.5 |
适用场景 | 多线程环境,需要处理共享变量的字符串操作 | 单线程环境,或方法内部的局部变量(最常见) |
API | 几乎完全相同,都继承自 | 几乎完全相同,都继承自 |
深入解析
1. 线程安全 (Thread Safety)
这是两者最本质的区别。
StringBuffer
: 它的关键方法(如append
,insert
,delete
等)都使用了synchronized
关键字进行修饰。这意味着多个线程在同时操作同一个StringBuffer
对象时,不会导致数据不一致的问题。一次只允许一个线程访问这些同步方法。
// StringBuffer 的 append 方法源码 (简化版)
@Override
public synchronized StringBuffer append(String str) {toStringCache = null;super.append(str);return this;
}
StringBuilder
: 它的方法没有使用synchronized
修饰。因此,在多线程环境下同时操作同一个StringBuilder
对象是不安全的,可能会导致数据混乱。但它也因此避免了同步带来的性能损耗。
// StringBuilder 的 append 方法源码 (简化版)
@Override
public StringBuilder append(String str) {super.append(str);return this;
}
2. 性能 (Performance)
由于 StringBuffer
的方法需要进行同步检查(获取锁、释放锁等操作),这会带来额外的性能开销。而在单线程情况下,这些同步措施是完全不必要的。
因此,在单线程环境下,StringBuilder
的性能显著优于 StringBuffer
。性能差异的程度取决于具体的操作和JVM实现,但在大量字符串拼接操作时,差距会变得非常明显。
3. 历史与版本
StringBuffer
从 Java 最初版本(JDK 1.0)就开始存在。StringBuilder
是在 JDK 1.5 中才被引入的。Sun 公司发现大部分字符串拼接操作都是在单线程内完成的(例如方法内的局部变量),为这些场景专门设计一个非线程安全的类可以大幅提升性能。
如何选择?
遵循一个非常简单直接的原则:
- 如果在多线程环境下操作字符串缓冲区(例如,类的成员变量,可能被多个线程同时修改),请使用
StringBuffer
来保证线程安全。 - 如果在单线程环境下操作字符串缓冲区(绝大多数情况,例如方法内部的局部变量),为了获得最佳性能,请使用
StringBuilder
。
现代Java开发中,95%以上的场景都是单线程的,所以 StringBuilder
是默认的、更常用的选择。
代码示例
单线程环境下的性能对比
public class PerformanceTest {public static void main(String[] args) {int count = 100000; // 操作次数// 测试 StringBufferlong startTime1 = System.currentTimeMillis();StringBuffer sb1 = new StringBuffer();for (int i = 0; i < count; i++) {sb1.append("hello");}long endTime1 = System.currentTimeMillis();System.out.println("StringBuffer 耗时: " + (endTime1 - startTime1) + " ms");// 测试 StringBuilderlong startTime2 = System.currentTimeMillis();StringBuilder sb2 = new StringBuilder();for (int i = 0; i < count; i++) {sb2.append("hello");}long endTime2 = System.currentTimeMillis();System.out.println("StringBuilder 耗时: " + (endTime2 - startTime2) + " ms");}
}
运行上述代码,你通常会看到 StringBuilder
的耗时比 StringBuffer
短。
多线程环境下的安全性
public class ThreadSafetyTest {public static void main(String[] args) throws InterruptedException {// 共享的 StringBuffer 对象StringBuffer safeBuffer = new StringBuffer();// 共享的 StringBuilder 对象 (危险!)StringBuilder unsafeBuilder = new StringBuilder();// 创建两个线程同时对它们进行追加操作Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {safeBuffer.append("A");unsafeBuilder.append("A");}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {safeBuffer.append("B");unsafeBuilder.append("B");}});t1.start();t2.start();t1.join();t2.join();// 检查 StringBuffer 的长度,预期是 2000System.out.println("StringBuffer 最终长度: " + safeBuffer.length()); // 几乎总是 2000// 检查 StringBuilder 的长度,很可能不是 2000,且可能抛出异常或产生乱序数据System.out.println("StringBuilder 最终长度: " + unsafeBuilder.length()); // 可能小于 2000}
}
在多线程环境下,StringBuilder
的行为是未定义的,可能导致数据丢失、长度错误甚至内部数组损坏而抛出异常。而 StringBuffer
能保证结果的正确性。
总结
方面 | StringBuffer | StringBuilder |
核心区别 | 线程安全(同步) | 非线程安全(不同步) |
性能 | 较低(有锁开销) | 高(无锁开销) |
使用场景 | 多线程共享数据 | 单线程或局部变量(首选) |
简单记:要安全用 Buffer,要性能用 Builder。现代开发无脑选 Builder,除非明确要共享给多线程。