ArrayList是否是线程安全的?
参考回答
ArrayList
不是线程安全的。
ArrayList
是 Java 中一个常用的动态数组实现,位于 java.util
包中。它的操作(如添加、删除、遍历)没有加锁或同步,因此在多线程环境下同时访问或修改 ArrayList
可能导致数据不一致或抛出异常(如 ConcurrentModificationException
)。
详细讲解与拓展
1. 为什么 ArrayList
不是线程安全的?
ArrayList
的内部实现没有对操作进行同步处理。例如,add()
、remove()
、get()
等方法都没有使用 synchronized
关键字或其他同步机制。
示例问题:
import java.util.ArrayList;
public class ArrayListThreadExample {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
// 创建两个线程同时操作 ArrayList
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
list.add(i);
}
});
Thread thread2 = new Thread(() -> {
for (int i = 1000; i < 2000; i++) {
list.add(i);
}
});
thread1.start();
thread2.start();
// 等待线程完成
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印结果
System.out.println("List size: " + list.size());
}
}
可能出现的问题:
- 数据不一致:由于线程争用,可能导致添加的元素数量与预期不符。
- 并发修改异常:如果一个线程在遍历
ArrayList
时,另一个线程对其进行修改,可能抛出ConcurrentModificationException
。
2. 如何使 ArrayList
线程安全?
方案 1:使用 Collections.synchronizedList
包装
可以使用 Collections.synchronizedList
将一个普通的 ArrayList
包装成线程安全的版本。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SynchronizedListExample {
public static void main(String[] args) {
List<Integer> list = Collections.synchronizedList(new ArrayList<>());
// 多线程操作
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
list.add(i);
}
});
Thread thread2 = new Thread(() -> {
for (int i = 1000; i < 2000; i++) {
list.add(i);
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("List size: " + list.size());
}
}
注意:
-
Collections.synchronizedList
的方法本身是线程安全的,但在迭代时仍需要手动同步:
synchronized (list) { for (Integer value : list) { System.out.println(value); } }
方案 2:使用 CopyOnWriteArrayList
CopyOnWriteArrayList
是一个线程安全的集合类,适合读多写少的场景。
代码示例:
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListExample {
public static void main(String[] args) {
CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
list.add(i);
}
});
Thread thread2 = new Thread(() -> {
for (int i = 1000; i < 2000; i++) {
list.add(i);
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("List size: " + list.size());
}
}
特点:
- 每次写操作都会创建一个新的底层数组,因此适合读操作多、写操作少的场景。
- 避免了手动同步的麻烦。
3. 性能对比
特性 | ArrayList |
Collections.synchronizedList |
CopyOnWriteArrayList |
---|---|---|---|
线程安全性 | 否 | 是 | 是 |
读操作性能 | 高 | 中等 | 高 |
写操作性能 | 高 | 中等 | 低 |
适用场景 | 单线程操作或无并发环境 | 多线程写操作较多 | 多线程读多写少的场景 |
总结
ArrayList
不是线程安全的,在多线程环境下可能导致数据不一致或抛出异常。- 如果需要线程安全的列表:
- 可以使用
Collections.synchronizedList
对ArrayList
进行包装。 - 或者直接使用
CopyOnWriteArrayList
,它在多线程环境下性能更优,适合读多写少的场景。
- 可以使用