CopyOnWriteArrayList源码阅读(1.8)

163 阅读2分钟

今天看下concurrent包下的CopyOnWriteArrayList。既然是concurrent包下的类,其肯定解决了一些并发上的问题------完全偏向提升读操作性能。CopyOnWriteArraySet的功能依托CopyOnWriteArrayList类的实现,在这里记录下。

类定义

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

可以看到其实现了List、RandomAccess、Cloneable 和 Serializable接口。

  • List CopyOnWriteArrayList本身是list这没什么好说的。
  • RandomAccess 可随机访问的标记,没有方法(可以区看看Collections类中的binarySearch方法)
  • Cloneable 可克隆的标记,没有方法
  • java.io.Serializable 可序列化

变量

//持有一把可重入锁,所有对数据元素有变化的方法都使用了这把锁来控制数据变化的线程安全
final transient ReentrantLock lock = new ReentrantLock();
private transient volatile Object[] array;//存储数据对象数组
private static final sun.misc.Unsafe UNSAFE;//又爱又恨的unsafe类
private static final long lockOffset;//用于定位持有锁对象在内存中的位置
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> k = CopyOnWriteArrayList.class;
            lockOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("lock"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
    //支持反序列化时重置锁
    private void resetLock() {
        UNSAFE.putObjectVolatile(this, lockOffset, new ReentrantLock());
    }

内部类

static final class COWIterator<E> implements ListIterator<E> //迭代器
private static class COWSubList<E> extends AbstractList<E> implements RandomAccess // 由subList方法产生,直接使用父亲数组的元素数组
private static class COWSubListIterator<E> implements ListIterator<E> //为COWSubList准备的迭代器

读写方法对比

    public E get(int index) {
        return get(getArray(), index);
    }
    @SuppressWarnings("unchecked")
    private E get(Object[] a, int index) {
        return (E) a[index];
    }
    
    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;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

示例选取了CopyOnWriteArrayList中的代表性读写方法。我们可以发现读方法并没有加锁,而写方法在加锁的情况下也没有影响到读操作。

总结

CopyOnWriteArrayList读操作无锁,写操作有锁。这是为了支持读多写少的场景。