CopyOnWriteArrayList 原理,优缺点

CopyOnWriteArrayList是Java并发包中提供的一种线程安全的ArrayList,其原理、优缺点可以归纳如下:

原理

CopyOnWriteArrayList的核心思想是"写时复制"(Copy-On-Write, COW),即在修改操作(添加、删除元素等)时,不是直接修改当前容器,而是先复制一份当前容器的副本,然后在副本上进行修改操作,最后再将原容器的引用指向修改后的新容器。这种机制保证了读操作不会受到写操作的阻塞,因此读操作是完全不用加锁的,性能较高。

优点

  1. 并发读性能高:由于读操作不需要加锁,且不会受到写操作的阻塞,因此在读操作远多于写操作的并发场景中,CopyOnWriteArrayList的性能表现非常出色。
  2. 线程安全:CopyOnWriteArrayList内部实现了线程安全的机制,无需外部同步,简化了并发编程的复杂性。
  3. 迭代器安全:CopyOnWriteArrayList提供了一个弱一致性的迭代器(Weakly Consistent Iterator),即在迭代过程中,即使列表发生了修改,迭代器仍然可以安全地进行,不会抛出ConcurrentModificationException异常。

缺点

  1. 内存开销大:由于每次写操作都会创建底层数组的一个副本,因此CopyOnWriteArrayList的内存开销相对较大。特别是在列表包含大量元素时,每次写操作都需要复制整个数组,这不仅会增加内存消耗,还可能导致Garbage Collection频繁发生,影响系统性能。
  2. 写操作性能低:由于每次写操作都需要复制整个底层数组,这使得写操作的时间复杂度是O(n),其中n是数组的大小。因此,在需要频繁写操作的场景下,CopyOnWriteArrayList的性能会非常低下。
  3. 数据实时性差: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
相关推荐
阿乾之铭几秒前
Spring Boot框架中的IO
java·spring boot·log4j·1024程序员节
百流12 分钟前
Pyspark中pyspark.sql.functions常用方法(4)
1024程序员节
qq210846295316 分钟前
【Ubuntu】Ubuntu22双网卡指定网关
1024程序员节
YueTann34 分钟前
APS开源源码解读: 排程工具 optaplanner II
1024程序员节
kinlon.liu42 分钟前
安全日志记录的重要性
服务器·网络·安全·安全架构·1024程序员节
爱编程— 的小李1 小时前
开关灯问题(c语言)
c语言·算法·1024程序员节
是程序喵呀1 小时前
Uni-App-02
uni-app·vue·1024程序员节
A_aspectJ2 小时前
Spring 框架中都用到了哪些设计模式?
spring·设计模式·1024程序员节
双子座断点2 小时前
QT 机器视觉 (3. 虚拟相机SDK、测试工具)
qt·1024程序员节
20岁30年经验的码农2 小时前
爬虫基础
1024程序员节