CopyOnWriteArrayList源码分析

CopyOnWriteArrayList源码分析

作用:CopyOnWriteArrayList(Copy-On-Write: 写入时复制)是一个线程安全的ArrayList,对其进行的修改操作都是先加锁然后在底层的一个复制数组上进行。

优点:经常被用于"读多写少"的并发场景,因为读取的时候不需要加锁,性能较好。读写分离,在使用迭代器迭代的时候不会抛出异常。

缺点:需要拷贝原数据,数据较大的时候容易引起频繁Full GC;写和读在不同的数组上,读取的是旧数组的数据。

成员变量
// 独占锁
final transient ReentrantLock lock = new ReetrantLock(); 

// 只能通过getArray/setArray方法来获取/修改array
private transient volatile Object[] array;

初始化

1. 创建空链表

public CopyOnWriteArrayList() {
    setArrayList(new Object[0]);
}


2. 根据给定的集合数据创建链表

public CopyOnWriteArrayList(Collections<? extends E> c) {
    Object[] elements;
    //如果是CopyOnWriteArrayList类,直接引用
    if (c.getClass() == CopyOnWriteArrayList.class) {
        elements = ((CopyOnWriteArrayList<?>)c).getArray();
    } else {
        elements = c.toArray();
        //c.toArray可能不会返回Object[]类型
        if (elements.getClass() != Object[].class) {
            //通过Arrays.copyOf复制数组
            elements = Arrays.copyOf(elements, elements.length, Object[].class);
        }
    }
    //设置array变量为elements
    setArray(elements);
}

部分方法源码

set(int index, E element) 修改array[index]的值

public E set(int index, E element) {
    final ReentrantLock lock = this.lock;
    //加锁
    lock.lock();
    try {
        //拿到成员变量array
        Object[] elements = getArray();
        // 获取elements[index]的值
        E oldValue = get(elements, index);
        //如果值发生了变化
        if (oldValue != element) {
            int len = elements.length;
            //先对elements复制一个拷贝
            Object[] newElements = Arrays.copyOf(elements, len);
            //更新值
            newElements[index] = element;
            //更新this.array = newElements
            setArray(newElements);
        } else {
            //为了保证volatile语义 
            setArray(elements);
        }
        //返回原始值
        return oldValue;
    } finally {
        //释放锁
        lock.unlock();
    }
}


add(E e) 添加元素到末尾

//和上面的set差不多 流程: 先获取锁->拷贝数组->修改拷贝数组->修改原始数组引用
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;
        //修改array引用
        setArray(newElements);
        return true;
    } finally {
        //释放锁
        lock.unlock();
    }
}

add(int index, E element) 按照下标位置添加 

public void add(int index, E element) {
        //加锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //获取原数组并计算长度
            Object[] elements = getArray();
            int len = elements.length;
            //如果下标超出数组长度并小于0抛出下标越界异常
            if (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);//末尾直接复制一个长度+1的新数组
            else {
                newElements = new Object[len + 1];//复制所有除了要添加的元素
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            newElements[index] = element;//根据下标添加新元素到新数组
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

remove(int index) 删除index位置的值

public E remove(int index) {
    final ReentrantLock lock = this.lock;
    //加锁
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        //index位置的值
        E oldValue = get(elements, index);
        //index之后需要移动的元素个数
        int numMoved = len - index - 1;
        //不需要移动
        if (numMoved == 0)
            setArray(Arrays.copyOf(elements, len - 1));
        else {
            //创建新数组
            Object[] newElements = new Object[len - 1];
            //拷贝删除位置前面的元素
            System.arraycopy(elements, 0, newElements, 0, index);
            //拷贝删除元素后面的元素
            System.arraycopy(elements, index + 1, newElements, index,
                                numMoved);
            //修改array引用
            setArray(newElements);
        }
        return oldValue;
    } finally {
        lock.unlock();
    }
}

emoveRange(int fromIndex, int toIndex) 根据范围删除元素 

public void removeRange(int fromIndex, int toIndex) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
			
			// 如果开始下标<0 或者结束下标大于数组长度或者结束下标小于开始下标
			// 抛下标越界异常
            if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
                throw new IndexOutOfBoundsException();
 
			// 计算新数组长度
            int newlen = len - (toIndex - fromIndex);
			// 判断结束下标是否是最后一个下标
            int numMoved = len - toIndex;
            if (numMoved == 0)
				//复制原数组
                setArray(Arrays.copyOf(elements, newlen));
            else {
				//新建一个长度为newlen的数组
                Object[] newElements = new Object[newlen];
				//将除了需要删除的下标元素复制到新数组
                System.arraycopy(elements, 0, newElements, 0, fromIndex);
                System.arraycopy(elements, toIndex, newElements,
                                 fromIndex, numMoved);
                setArray(newElements);
            }
        } finally {
            lock.unlock();
        }
    }
 

retainAll(Collection<?> c) 在当前集合保留传入集合中的元素,当前元素和集合中元素做交集
 

public boolean retainAll(Collection<?> c) {
    //集合为null抛出空指针异常
     if (c == null) throw new NullPointerException();
     final ReentrantLock lock = this.lock;
     lock.lock();
     try {
	    //获取当前集合对象中数组
        Object[] elements = getArray();
        int len = elements.length;
        //当前数组不为空
        if (len != 0) {
        // temp array holds those elements we know we want to keep
           int newlen = 0;
           //创建中间数组
           Object[] temp = new Object[len];
	       //遍历数组
           for (int i = 0; i < len; ++i) {
              Object element = elements[i];
              //判断集合c中是否包含elements中的元素
              if (c.contains(element))
                //包含则将元素存入中间数组
                temp[newlen++] = element;
              }
              //中间数组下标+1之后不等于elements数组长度
              if (newlen != len) {
                //使用newlen作为新数组长度复制
                setArray(Arrays.copyOf(temp, newlen));
                return true;
              }
            }
           return false;
      } finally {
          lock.unlock();
      }
}

clear()方法 清空集合

public void clear() {
     final ReentrantLock lock = this.lock;
     lock.lock();
     try {
       	//指向一个新建长度为0的数组
        setArray(new Object[0]);
     } finally {
        lock.unlock();
     }
}

 addAll(int index, Collection<? extends E> c) 按照下标位置批量添加元素

public boolean retainAll(Collection<?> c) {
   //集合为null抛出空指针异常
   if (c == null) throw new NullPointerException();
     final ReentrantLock lock = this.lock;
     lock.lock();
     try {
	    //获取当前集合对象中数组
        Object[] elements = getArray();
        //获取数组的长度
        int len = elements.length;
        //如果当前数组不为空
        if (len != 0) {
          // temp array holds those elements we know we want to keep
          int newlen = 0;
          //创建一个临时数组
          Object[] temp = new Object[len];
		  //遍历数组
          for (int i = 0; i < len; ++i) {
              Object element = elements[i];
              //判断集合c中是否包含elements中的元素
              if (c.contains(element))
                 //包含则将元素存入临时数组
                 temp[newlen++] = element;
              }
              //如果临时数组是否等于elements数组长度
              if (newlen != len) {
               //不等于则使用newlen作为新数组长度复制
               setArray(Arrays.copyOf(temp, newlen));
               return true;
              }
          }
         return false;
     } finally {
         lock.unlock();
    }
}
  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CopyOnWriteArrayListJava 中的一个线程安全的集合类,它的实现原理是在写操作时创建一个新的数组来存储数据,从而实现了读写分离,保证了读操作的线程安全性。 具体的实现原理如下: 1. CopyOnWriteArrayList 内部使用一个数组来存储数据。 2. 在进行写操作(添加、修改、删除)时,先创建一个新的数组,将原数组的数据复制到新数组中,并在新数组上进行写操作。 3. 写操作完成后,将新数组赋值给原数组,从而实现了数据的更新。 4. 在进行读操作时,直接读取原数组的数据,保证了读操作的线程安全性。 CopyOnWriteArrayList 的实现原理保证了读操作的线程安全性,因为读操作不会对原数组进行修改,而是读取原数组的数据。但是写操作会创建一个新的数组,因此写操作的性能相对较低。 范例:<<引用[1]:CopyOnWriteArrayList原理。 文章目录 一、 CopyOnWriteArrayList介绍二、 CopyOnWriteArrayList原理三、 CopyOnWriteArrayList 属性介绍四、 构造器以及添加add方法五、 get(int index)六、 remove(int index)七、 遍历 。 引用:CopyOnWriteArrayList实现原理及源码分析。1 List list = Collections.synchronizedList(new ArrayList()); 。 CopyOnWriteArrayList 实现原理如下: 1. 内部使用一个数组来存储数据。 2. 在进行写操作时,先创建一个新的数组,将原数组的数据复制到新数组中,并在新数组上进行写操作。 3. 写操作完成后,将新数组赋值给原数组,从而实现了数据的更新。 4. 在进行读操作时,直接读取原数组的数据,保证了读操作的线程安全性。 5. 写操作会创建一个新的数组,因此写操作的性能相对较低。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值