CopyOnWriteArrayList

2021/01/11 posted in  并发容器

JDK 提供一种线程安全的List,多线程环境下可以直接使用,通过锁 + 数组拷贝 + volatile 保证线程的安全,所有的操作都是线程安全的,每次数组操作都是拷贝一分出来,在新数组上进行操作,之后再赋值回去;迭代过程不会影响原来的数组

源码解析

新增方法

  1. 加锁 : 保证同一时刻只有一个线程操作
  2. 数组拷贝 : 保证数组地址能改变,触发volatile的可见性
  3. volatile : 保证被修改后其他线程可以感知
    // 添加到队尾
    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 里面释放锁
        } finally {
            lock.unlock();
        }
    }
// 增加到指定位置
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];
                // 拷贝 0 到 index
                System.arraycopy(elements, 0, newElements, 0, index);
                // 拷贝 index + 1 到队尾
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            newElements[index] = element;
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

批量删除

不是采用for循环中单独删除每个值,先对数组中的值进行循环判断,把不需要删除的值放到临时数组中

public boolean removeAll(Collection<?> c) {
        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];
                    if (!c.contains(element))
                        temp[newlen++] = element;
                }
                // 拷贝新数组,变相的删除满足条件的元素
                if (newlen != len) {
                    setArray(Arrays.copyOf(temp, newlen));
                    return true;
                }
            }
            return false;
        } finally {
            lock.unlock();
        }
    }