java多线程之CopyOnWriteArrayList源码解析

前言

CopyOnWriteArrayList是一个线程安全的ArrayList,对ArrayList内部结构不太清楚的可以看看博主的这篇文章:从源码分析java容器之ArrayList,看完这篇文章,你会知道为什么ArrayList是非线程安全的。然后你再来看这篇文章,看看CopyOnWriteArrayList是如何保证线程安全的。

1、结构图

java多线程之CopyOnWriteArrayList源码解析_第1张图片
从结构图中,我们可以看出CopyOnWriteArrayList本质上还是一个List,内部数据结构还是数组。

2、分析源码

2.1、属性

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 8673264195747942595L;
    //增加了一个ReentrantLock互斥锁
    //我们应该能够猜到是保证增删的线程安全,保证了写的线程安全
    final transient ReentrantLock lock = new ReentrantLock();
	//内部数组增加了volatile修饰
	//增加了数组多线程中在内存的可见性,保证了读的线程安全
    private transient volatile Object[] array;
    //以下省略……
}

2.2、构造方法

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

public CopyOnWriteArrayList(Collection<? extends E> c) {
    Object[] elements = c.toArray();
    if (elements.getClass() != Object[].class)
        elements = Arrays.copyOf(elements, elements.length, Object[].class);
    setArray(elements);
}

public CopyOnWriteArrayList(E[] toCopyIn) {
    setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}

2.3、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;
        //新数组指向Volatile修饰的array
        setArray(newElements);
        return true;
    } finally {
    	//释放锁
        lock.unlock();
    }
}

上面的add()方法步骤是:

  1. 先copy旧数组到新数组
  2. 然后写入新增的元素
  3. 再将新数组指向Volatile修饰的array数组

有木有突然明白为什么它叫CopyOnWriteArrayList。

3、总结

  1. CopyOnWriteArrayList内部数据结构是数组。
  2. CopyOnWriteArrayList内部有一个ReentrantLock对象的成员变量lock,lock保证了写和删除操作的线程安全。
  3. CopyOnWriteArrayList内部的数组是用Volatile修饰,保证了读操作的线程安全。
  4. CopyOnWriteArrayList的扩容是每次+1,适合的场景是读多写少的场景。

结束语

通过本篇的分析,我们知道了CopyOnWriteArrayList相比ArrayList,如何保证了线程安全。

下一篇,我们将分析CopyOnWriteArraySet的实现。

你可能感兴趣的:(#,java多线程)