CopyOnWriteArrayList是Java并发包中提供的一种线程安全的ArrayList,其原理、优缺点可以归纳如下:
原理
CopyOnWriteArrayList的核心思想是"写时复制"(Copy-On-Write, COW),即在修改操作(添加、删除元素等)时,不是直接修改当前容器,而是先复制一份当前容器的副本,然后在副本上进行修改操作,最后再将原容器的引用指向修改后的新容器。这种机制保证了读操作不会受到写操作的阻塞,因此读操作是完全不用加锁的,性能较高。
优点
- 并发读性能高:由于读操作不需要加锁,且不会受到写操作的阻塞,因此在读操作远多于写操作的并发场景中,CopyOnWriteArrayList的性能表现非常出色。
- 线程安全:CopyOnWriteArrayList内部实现了线程安全的机制,无需外部同步,简化了并发编程的复杂性。
- 迭代器安全:CopyOnWriteArrayList提供了一个弱一致性的迭代器(Weakly Consistent Iterator),即在迭代过程中,即使列表发生了修改,迭代器仍然可以安全地进行,不会抛出ConcurrentModificationException异常。
缺点
- 内存开销大:由于每次写操作都会创建底层数组的一个副本,因此CopyOnWriteArrayList的内存开销相对较大。特别是在列表包含大量元素时,每次写操作都需要复制整个数组,这不仅会增加内存消耗,还可能导致Garbage Collection频繁发生,影响系统性能。
- 写操作性能低:由于每次写操作都需要复制整个底层数组,这使得写操作的时间复杂度是O(n),其中n是数组的大小。因此,在需要频繁写操作的场景下,CopyOnWriteArrayList的性能会非常低下。
- 数据实时性差:CopyOnWriteArrayList在每次写操作时都要复制整个数组,因此不适合用于需要实时读取最新数据的场景。在读取过程中,如果另一个线程完成了写操作,但由于读操作是在旧数组上进行的,读取线程依然会获取到写操作之前的数据。
使用场景
CopyOnWriteArrayList最适合的场景是读多写少的场景,如配置管理、事件监听器列表 等。在这些场景中,读操作非常频繁,而写操作相对较少。由于读操作无锁且非常高效,因此CopyOnWriteArrayList能够提供优异的性能。
实例
java
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListExample {
public static void main(String[] args) {
// 创建一个CopyOnWriteArrayList对象
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
// 添加元素到列表中
list.add("Alice");
list.add("Bob");
list.add("Charlie");
// 遍历并打印列表中的元素
System.out.println("Initial List:");
for (String element : list) {
System.out.println(element);
}
// 创建并启动多个线程,一个用于添加元素,一个用于删除元素
Thread thread1 = new Thread(new AddElementTask(list));
Thread thread2 = new Thread(new RemoveElementTask(list));
thread1.start();
thread2.start();
// 等待线程执行完毕
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 遍历并打印最终的列表内容
System.out.println("Final List:");
for (String element : list) {
System.out.println(element);
}
}
// 添加元素的线程任务
static class AddElementTask implements Runnable {
private CopyOnWriteArrayList<String> list;
public AddElementTask(CopyOnWriteArrayList<String> list) {
this.list = list;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
list.add("Element " + i);
System.out.println("Added Element " + i);
try {
Thread.sleep(100); // 模拟一些延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 删除元素的线程任务
static class RemoveElementTask implements Runnable {
private CopyOnWriteArrayList<String> list;
public RemoveElementTask(CopyOnWriteArrayList<String> list) {
this.list = list;
}
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
if (list.size() > 0) {
String removedElement = list.remove(0);
System.out.println("Removed " + removedElement);
}
try {
Thread.sleep(200); // 模拟一些延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
运行结果
bash
Initial List:
Alice
Bob
Charlie
Removed Alice
Added Element 1
Added Element 2
Removed Bob
Added Element 3
Added Element 4
Removed Charlie
Added Element 5
Final List:
Element 1
Element 2
Element 3
Element 4
Element 5
与List性能对比(add)
java
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListCompareListExample {
public static void main(String[] args) {
CopyOnWriteArrayList<String> copList = new CopyOnWriteArrayList<>();
long begin = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
copList.add(String.valueOf(i));
}
long end = System.currentTimeMillis();
System.out.println("CopyOnWriteArrayList:"+(end - begin));
List<String> list = new CopyOnWriteArrayList<>();
long beginList = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
list.add(String.valueOf(i));
}
long endList = System.currentTimeMillis();
System.out.println("List:"+(endList - beginList));
}
}
运行结果
bash
CopyOnWriteArrayList:2128
List:1936