Java并发编程(十二) CopyOnWriteArrayList

下面我们来说一下CopyOnWriteArrayList。这个类是一个线程安全的集合,通过copy-on-write机制实现的,下面我们就来看一下CopyOnWriteArrayList是怎么实现的。
先来看一下CopyOnWriteArrayList的属性

/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();

/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;

有一个ReentrantLock,还有一个Object数组
下面我们来看一下get方法

public E get(int index) {
    return get(getArray(), index);
}

再到getArray方法

final Object[] getArray() {
    return array;
}

就是从数组对应的位置取出元素返回
看到这里,你可能会觉得好像也没有什么特别的啊,是怎么实现copy-on-write机制的呢?别急,我们下面来看set方法

public E set(int index, E element) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        E oldValue = get(elements, index);

        if (oldValue != element) {
            int len = elements.length;
            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();
    }
}

先加锁,然后从数组里取出原来的元素,然后判断原来的元素不等于要set的元素的话,就用Arrays的copyOf方法复制一份新的数组,把要set的元素set进去,并把新的数组替换掉原来的数组,否则直接把原来的数组再放回去,确保这个过程的内存可见性,然后解锁。
再来看一下add方法

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();
    }
}

先加锁,然后取到当前的数组,调用Arrays的copyOf方法复制一份新的数组,把元素添加进去,然后解锁。
再来看一下在特定位置add的方法

public void add(int index, E element) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        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);
        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();
    }
}

这个要复杂一点,先加锁,然后获取到当前的数组,判断数组的位置,然后用当前数组的长度-索引值,得到移动的元素的数量,如果没有元素被移动,则调用Arrays的copyOf方法复制一份新的数组,否则创建一个比原来数组容量大1的新的数组,然后调用System.arraycopy方法复制一份新的数组,然后将元素添加到对应的位置上,然后将新的数组替换掉原来的数组,然后解锁。
再来看一下remove方法

public E remove(int index) {
    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;
        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);
            setArray(newElements);
        }
        return oldValue;
    } finally {
        lock.unlock();
    }
}

先加锁,然后获取到原来的元素,然后用数组的容量-索引值-1算出被移动的元素的数量,然后判断如果没有元素被移动,则调用Arrays的copyOf方法复制一份新的数组,并替换掉原来的数组,否则创建一个新的数组,容量是原来的容量-1,然后调用System.arraycopy方法复制一份新的数组,然后将要删除的元素的后一个元素往前移动,完成元素的删除,然后将新的数组替换掉原来的数组,然后解锁。
CopyOnWriteArrayList的分析就到这里了。

你可能感兴趣的:(Java并发编程(十二) CopyOnWriteArrayList)