CopyOnWriteArrayList核心源码解析(增删改)

CopyOnWriteArrayList是一种线程安全的集合实现,通过数组复制实现并发写操作的隔离,确保读操作无锁。这种策略适合读多写少的场景,但会增加内存开销。添加、删除和修改元素时,它会创建新数组并加锁进行操作,确保线程安全。
摘要由CSDN通过智能技术生成
  • 概述:

CopyOnWrite简称为COW,是一种用于集合的并发访问优化策略。CopyOnWriteArrayList时通过数组的复制来实现线程安全性。在进行操作时,恢复至出一个新的数组并且在新的数组上进行修改,避免了原始数据的修改以此来保证线程安全。但是,因为每次都要对数组进行复制操作,带来一些过多的性能开销。除此之外,它的读操作是无锁的,所以CopyOnWriteArrayList适用于读多写少的场景。

  • 基本思想:

当代码往一个集合容器中写入元素时(添加、修改、删除),并不是在原本的集合容器中写入数据,而是将当前的集合容器的内容复制到一个新的容器中,然后在新的容器中写入元素之后再将原容器的引用指向新的容器。

  • add()方法:

在添加元素时,先使用ReentrantLock关键字对关键代码块进行加锁,保证修改时不会出现线程竞争,再用Arrays.copyOf()方法创建出一个新的数组,长度比原来多1,将e添加到新数组的最后位置,使用setArray()方法将原来数组的指向设置为新数组,最后返回true表示添加成功。

源代码解析如下:

public boolean add(E e) {
    	//定义ReentrantLock锁
        final ReentrantLock lock = this.lock;
        //加锁
        lock.lock();
        try {
        	获得旧值数组elements
            Object[] elements = getArray();
            //获得旧值数组长度
            int len = elements.length;
            //使用Arrays.copyOf()复制出一个新的数组newElements
            //长度+1
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            //将e放入新数组的最后一个位置
            newElements[len] = e;
            //指向新的数组
            setArray(newElements);
            //返回true
            return true;
        } finally {
        	//使用finally关键字是无论上文有没有异常都会解锁
        	//解锁
            lock.unlock();
        }
    }
  • remove()方法:

在移除元素时,使用ReentrantLock关键字对关键代码块进行加锁,保证修改时不会出现线程竞争。通过index()找到索引位置,索引为-1表示该列表中不存在该元素。否则使用remove()方法删除该索引处的元素。最后返回true表示删除成功。

源代码解析如下:

    public E remove(int index) {
    	//定义ReentrantLock锁
        final ReentrantLock lock = this.lock;
        //加锁
        lock.lock();
        try {
        	//获取旧数组数据以及长度
            Object[] elements = getArray();
            int len = elements.length;
            //得到旧值
            E oldValue = get(elements, index);
            //判断删除数据是否最后一个元素
            int numMoved = len - index - 1;
            //为最后一位,将旧数组内容复制到len-1
            if (numMoved == 0)
                setArray(Arrays.copyOf(elements, len - 1));
            else {
            	//否则创建新的数组并且长度—1
            	Object[] newElements = new Object[len - 1];
            	//根据下标位置得到要移除数据之外的数组
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index + 1, newElements, index,
                                 numMoved);
                //返回新的数组
                setArray(newElements);
            }
            //返回旧值
            return oldValue;
        } finally {
            lock.unlock();
        }
    }
  • set()方法:

通过get()方法获取到索引处的旧值oldvalue,如果oldvalue不等于新值element,则使用Arrays.copyOf()方法创建出一个新的数组element,长度和原数组一样,再将新值element存入数组elements中的索引处,使用setArray()方法将原来数组的指向设置为新数组,最后返回旧值oldvalue。

源代码解析如下:

public E set(int index, E element) {
    	//定义ReentrantLock锁
        final ReentrantLock lock = this.lock;
        //加锁
        lock.lock();
        try {
        	//获得旧值数组elements
            Object[] elements = getArray();
            //根据索引回去旧值oldValue
            E oldValue = get(elements, index);
            //旧值不等于新值element
            if (oldValue != element) {
            	//长度与旧值一致
                int len = elements.length;
                //使用Arrays.copyOf()复制出一个新的数组newElements
                Object[] newElements = Arrays.copyOf(elements, len);
                //新值存入新数组
                newElements[index] = element;
                //指向新的数组
                setArray(newElements);
            } else {
                // Not quite a no-op; ensures volatile write semantics
                setArray(elements);
            }
            //返回旧值
            return oldValue;
        } finally {
        	//解锁
            lock.unlock();
        }
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猿究院懒羊羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值