Java CopyOnWriteArrayList Copy on Write(写时复制)

本文深入剖析了Java中CopyOnWriteArrayList的工作原理,重点介绍了其如何利用写时复制机制来实现线程安全的集合操作,同时探讨了该机制带来的数据一致性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java CopyOnWriteArrayList Copy on Write(写时复制)分析

Copy on Write这种机制用在集合上,当读取元素,先复制原有集合内容,在集合的某个副本上进行遍历操作。当发生写操作时,首先复制原有集合内容生成一个副本,随后在这个副本上进行写操作。

        CopyOnWriteArrayList<Integer> ids = new CopyOnWriteArrayList<>();
        ids.add(5);
        for (Integer i : ids) {
            System.out.println(i);
            ids.add(6);
        }

        for (Integer i : ids) {
            System.out.println(i);
        }

以上代码以CopyOnWriteArrayList为例。我们知道使用foreach语法糖进行集合遍历时,实际上是通过迭代器对象来进行遍历。

CopyOnWriteArrayList内部维护了array对象,并COWIterator进行元素迭代。访问通array对象内的元素。

    private transient volatile Object[] array;

当循环开始首先创建COWIterator对象,这时COWIterator内部维护的snapshot对象指向CopyOnWriteArrayList的array对象。而在循环执行过程中snapshot将不会发生变化。因此即使在循环中对CopyOnWriteArrayList进行修改,也不会影响snapshot结果。所以不会造成ConcurrentModificationException的问题。

        private final Object[] snapshot;
        /** Index of element to be returned by subsequent call to next.  */
        private int cursor;

        private COWIterator(Object[] elements, int initialCursor) {
            cursor = initialCursor;
            snapshot = elements;
        }

当对list执行修改操作,这是会新创建一段存储空间,并拷贝原有数据。随后在这个新的副本空间上进行修改。当修改完毕执行setArray,修改
CopyOnWriteArrayList中array成员,array指向新的存储空间。而迭代器在初始化时保存的是旧的存储空间引用。因此写操作不会对当前循环发生影响。当再次循环遍历时可以看到新增加的元素输出。

    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

在这里插入图片描述

此外我们看到add操作是加了锁。当多线程操作同时发生修改操作,若不加锁会导致数据发生覆盖,添加数据丢失。

总结

Copy on write提供了一种机制,当遍历一个集合时,首先纪录集合当时的快照引用。那么遍历就是对集合某个时刻快照进行遍历。当发生写操作会生成新的副本,修改操作发生在新的副本上,而不对原有存储空间修改。这种思路下,读写处理了不同的数据存储区域, 避免了并发修改异常的问题。但这种方式也会造成数据读取时短暂不一致状态。

参考

jdk1.8 java.util.concurrent.CopyOnWriteArrayList

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值