Java集合框架之CopyOnWrite

背景

我们知道,多个线程操作ArrayList的时候,会抛出异常:

ArrayList中有一个failfast机制。比如一开始确定集合长度,同时会决定遍历的次数。但是如果某个线程读取的时候,有其他线程又插入元素,就破坏了一开始读到的快照,然后判断之前读到的快照和现在数组不同,就会直接抛出异常。

所以Java中增加了线程安全的List:copyOnWriteArrayList.

copyOnWriteArrayList可以保证多个线程同时读写是线程安全的。

我们自己实现的话,可能会考虑加reentrantLock。但是这样锁的粒度太大了,性能较低。

v1.0:还有一个中思路是使用ReentrantReadWriteLockl. 写的时候lock.writelock(), 读读不互斥,读写互斥, 写写互斥。
这个方案在读写都比较大的场景下是可以的。

但是如果在读多写少的情况下,使用读写锁效率不是很高。

这个时候就可以考虑使用CopyOnWrite机制。

核心思想

读写分离,空间换时间,避免为保证并发安全导致的激烈的锁竞争。

划关键点:

1、CopyOnWrite适用于读多写少的情况,最大程度的提高读的效率;
2、CopyOnWrite是最终一致性,在写的过程中,原有的读的数据是不会发生更新的,只有新的读才能读到最新数据;
3、如何使其他线程能够及时读到新的数据,需要使用volatile变量;
4、写的时候不能并发写,需要对写操作进行加锁;
5、保证数据的最终一致性,允许读到脏数据。

在这里插入图片描述

源码原理

写时复制

在这里插入图片描述
我们来看一下添加的源码:

/**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
    	// 只能有一个线程进行写操作,所以需要加锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            // 复制一个新的数组,长度比原数组长度多1
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            // 将旧的数据设置到新数组的最后一位上
            newElements[len] = e;
            // 使用系数组替换原来的旧数组,并且等待所有读线程读完后丢弃旧的数组。
            // 注意,此时读的线程读到的依然是旧的数组。只有一个替换旧数组后,新来的读线程才会读取到新的数据
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }
    
----------------------------------------
final void setArray(Object[] a) {
        array = a;
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值