欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 金融 > 并发集合(二):CopyOnWriteArrayList

并发集合(二):CopyOnWriteArrayList

2025/4/19 9:40:27 来源:https://blog.csdn.net/liang8999/article/details/141859960  浏览:    关键词:并发集合(二):CopyOnWriteArrayList

1、CopyOnWriteArrayList介绍

      CopyOnWriteArrayList 是一个线程安全的ArrayList。
      CopyOnWriteArrayList 是基于Lock锁和线程副本的形式来保证线程安全的,
      在写数据时,先获取Lock锁,然后复制一个副本,添加数据时,是往副本数组中添加数据,最        后再将副本数组赋值给自身array属性,这样可以保证读写分离(读写完全独立),从而提高          读写性能
      CopyOnWriteArrayList 是弱一致性的,写操作先操作,但是副本还没落CopyOnWriteArrayList        的 array属性中,此时读操作是无法读取刚写入的数据的。 

       注意:
              CopyOnWriteArrayList 每次写操作都要复制一个副本,若业务场景是写多多少,并且数                  据量比较大时,则尽量避免使用 CopyOnWriteArrayList;因为这里会创建大量的数组副                  本,比较占用内存资源;数组数据量很大时,数组的复制也是很消耗内存资源的

2、核心属性&方法

      CopyOnWriteArrayList 比较简单,只看下2个属性,2个设置array属性的方法及1个无参构造

      函数就行了,如下所示:

//lock锁,写操作时先获取锁
final transient ReentrantLock lock = new ReentrantLock();
//真正保存数据的数组
private transient volatile Object[] array;/*** 获取array属性*/final Object[] getArray() {return array;}/*** 替换array属性*/final void setArray(Object[] a) {array = a;}public CopyOnWriteArrayList() {//默认数组长度为0//因为每次写数据时,都会构建一个全新的数组,所以在 CopyOnWriteArrayList 中也没有数组扩容的操作setArray(new Object[0]);}

3、读操作

     CopyOnWriteArrayList 读操作就是执行get方法,通过数组的索引位置来获取数据;

     具体方法如下:

            

4、写操作

     CopyOnWriteArrayList 写操作是基于Lock 锁+数组副本来保证写操作的线程安全的;

      且与读操作隔离,相互独立,互不影响。

      写操作方法如下:

      4.1)add(E e) 方法

               默认在副本数组的末尾添加数据

   /*** 添加数据,不指定索引位置,默认是在最后边添加数据*/public boolean add(E e) {final ReentrantLock lock = this.lock;//获取锁lock.lock();try {//构建array属性的副本Object[] elements = getArray();int len = elements.length;//创建新的数组,并将副本elements 的数据赋值到 newElements 中Object[] newElements = Arrays.copyOf(elements, len + 1);//添加数据,在最后添加newElements[len] = e;//将 newElements 赋值给array属性setArray(newElements);return true;} finally {//释放锁lock.unlock();}}

      4.2)add(E e,int index) 方法

               在指定索引位置index处添加数组,但不会覆盖索引index原有的数据

   /*** 在指定位置index处添加数据,但不会覆盖数据*/public void add(int index, E element) {final ReentrantLock lock = this.lock;//加锁lock.lock();try {//获取array属性的值,即复制 array 副本Object[] elements = getArray();int len = elements.length;//判断索引位置是否合法 0<= index <= lenif (index > len || index < 0)throw new IndexOutOfBoundsException("Index: "+index+", Size: "+len);Object[] newElements;int numMoved = len - index;if (numMoved == 0) //表示新数据需要放到当前数组的最后边newElements = Arrays.copyOf(elements, len + 1);else {//表示当前数据需要放到索引在[0,len-1] 的范围内//创建新的数组,长度是array属性长度加1newElements = new Object[len + 1];//将老数组elements的[0,index) 位置的数据复制到新数组 newElements 的[0,index)处System.arraycopy(elements, 0, newElements, 0, index);//将老数组elements的[index,len) 位置的数据复制到新数组 newElements 的[index+1,len]处System.arraycopy(elements, index, newElements, index + 1,numMoved);}//数据正常放到索引位置index处newElements[index] = element;setArray(newElements);} finally {//释放锁lock.unlock();}}

5、移除数据

     CopyOnWriteArrayList 移除数据有2种情况,一是根据索引index删除,二是删除指定数据,若

     数据不存在,则返回null

     5.1、remove(int index):据索引index删除指定位置的数据

              

public E remove(int index) {final ReentrantLock lock = this.lock;//加锁lock.lock();try {//获取array属性值Object[] elements = getArray();int len = elements.length;//获取指定索引位置index处的值E oldValue = get(elements, index);//删除数据的位置int numMoved = len - index - 1;if (numMoved == 0) //要删除的位置是数组最后的一个数据setArray(Arrays.copyOf(elements, len - 1));else {//创建新的数组Object[] newElements = new Object[len - 1];//删除数据//将旧数组 elements 的[0,index)处的数据复制到新数组 newElements 的[0,index)处System.arraycopy(elements, 0, newElements, 0, index);//将旧数组 elements 的[index+1,len)处的数据复制到新数组 newElements 的[index,len-1)处System.arraycopy(elements, index + 1, newElements, index,numMoved);//设置array属性setArray(newElements);}//返回删除的数据return oldValue;} finally {lock.unlock();}}

     5.2、remove(Object o):删除指定的数据

public boolean remove(Object o) {//获取array的副本Object[] snapshot = getArray();//获取要删除数据的位置索引,若数据不存在,则返回-1//todo 注意:若是并发场景下,这里获取到的数据o的位置index不一定正确int index = indexOf(o, snapshot, 0, snapshot.length);return (index < 0) ? false : remove(o, snapshot, index);}private boolean remove(Object o, Object[] snapshot, int index) {final ReentrantLock lock = this.lock;//加锁lock.lock();try {//新获取array的副本Object[] current = getArray();int len = current.length;// findIndex 是if 代码块的名称// snapshot != current 表示并发下array已经被其他线程修改了,// 那么参数传递来的删除数据o的位置index也不正确,需要重新获取indexif (snapshot != current) findIndex: {//若 index>len :则说明array被其他线程删除了index之后的数据//index<= len: 则表示array可能新增、删除了数据int prefix = Math.min(index, len);for (int i = 0; i < prefix; i++) {//current[i] != snapshot[i]:表示数组array在并发下发生了改变//eq(o, current[i]):判断当前i位置的数据与要删除的数据o是否一致,若一致,则将i赋值给indexif (current[i] != snapshot[i] && eq(o, current[i])) {index = i;//退出if块break findIndex;}}//如果for循环结束还没找到要删除的数据,则表示数组array发生了改变//删除数据不存在if (index >= len)return false;//判断当前index位置的数据是否是要删除的数据,若是,则跳出if 代码块 findIndexif (current[index] == o)break findIndex;//重新查找删除数据o的索引位置index = indexOf(o, current, index, len);//没找到if (index < 0)return false;}//删除数据Object[] newElements = new Object[len - 1];System.arraycopy(current, 0, newElements, 0, index);System.arraycopy(current, index + 1,newElements, index,len - index - 1);//设置array属性setArray(newElements);return true;} finally {//释放锁lock.unlock();}}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词