CopyOnWriteArrayList写时复制原理

CopyOnWriteArrayList读取时不加锁只是写入和删除时加锁,所以一个线程X读取的时候另一个线程Y可能执行remove操作。remove操作首先要获取独占锁,然后进行写时复制操作,就是复制一份当前的array数组,然后在复制的新数组里面删除线程X通过get访问的元素,比如:1。删除完成后让array指向这个新的数组。
在线程x执行get操作的时候并不是直接通过全局array访问数组元素而是通过方法的形参a访问的,a指向的地址和array指向的地址在调用get方法的那一刻是一样的,都指向了堆内存的数组对象。之后改变array指向的地址并不影响get的访问,因为在调用get方法的那一刻形参a指向的内存地址就已经确定了,不会改变。

    public E get(int index) {
        return get(getArray(), index);
    }
    private E get(Object[] a, int index) {
        //形参直接指向了原数组,array引用地址的改变并不影响这里取值
        return (E) a[index];
    }

    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);
                //将array指向新数组
                setArray(newElements);
            }
            return oldValue;
        } finally {
            lock.unlock();
        }
    }
image.png

删除完成后让array指向新的数组。这个时候{1,2,3}这个数组的引用计数不为0而是为1,线程x还在使用它。


image.png

所以,虽然线程y已经删除了index处的元素但是线程x的获取操作还是会返回index处的元素。

你可能感兴趣的:(CopyOnWriteArrayList写时复制原理)