String的+运算符,底层实现是通过StringBuilder.append()方法来实现的。
但是StringBuilder不是线程安全的,若要在多线程环境下实现程序的正确性,有以下几种方法:
- 使用StringBuffer类;
- 手动同步 StringBuilder 的操作,使用synchronized关键字;
- 使用ThreadLocal,每个线程都有自己的局部StringBuilder变量
以下是一个StringBuilder在多线程场景下会出现的问题示例:
/**
* 1. 验证使用线程不安全的StringBuilder类会产生的后果:输出结果会错乱,每次输出的Final result length 都不一样
* 2. 使用线程安全的StringBuffer类不会有这种问题,输出的Final result length固定为24000
* 3. 如果要使用线程不安全的StringBuilder类,需要使用synchronized关键字来保证线程安全
* 4. 为什么要使用线程安全的StringBuffer类:StringBuilder 的方法(如 append, delete, insert, replace 等)都没有进行同步(synchronized),因此如果多个线程同时访问并操作同一个 StringBuilder 实例,会导致数据不一致或出现其他意想不到的行为。
*/
public class StringBuilderExample {
//线程不安全的
private static StringBuilder sb = new StringBuilder();
//线程安全
// private static StringBuffer sb = new StringBuffer();
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
synchronized (sb) {
sb.append(Thread.currentThread().getName()).append(" ");
}
}
};
Thread thread1 = new Thread(task, "Thread1");
Thread thread2 = new Thread(task, "Thread2");
Thread thread3 = new Thread(task, "Thread3");
thread1.start();
thread2.start();
thread3.start();
thread1.join();
thread2.join();
thread3.join();
// 输出结果
System.out.println("Final result length: " + sb.length());
System.out.println("Final result: " + sb);
}
}
StringBuffer和StringBuilder的区别主要体现在线程安全性和性能上,用法基本相同,以下表格是详细对比:
| 特性 | StringBuffer | StringBuilder |
|---|---|---|
| 线程安全性 | 是 | 否 |
| 性能 | 较低(多线程场景适合) | 较高(单线程场景适合) |
| 同步机制 | 使用synchronized | 无同步 |
| 适用场景 | 多线程环境 | 单线程环境 |