第五章、Java并发包中并发List源码剖析

202 阅读2分钟

1.copyOnWriteArrayList

并发包中的并发List只有CopyOnWriteArrayList。CopyOnWriteArrayList是一个线程安全的ArrayList,对其进行的修改操作都是在底层的一个复制的数组(快照)上进行的,也就是使用了写时复制策略。

clipboard.png

2.源码方法解析

  • 无参构造函数
// 在内部创建了一个大小为0的Object数组作为array的初始值 
public CopyOnWriteArrayList() { 
    setArray(new Object[0]); 
}
  • 有参构造函数
// 创建一个list,入参为toCopyIn副本
public CopyOnWriteArrayList(E[] toCopyIn) {
    setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}

// 入参为一个集合,将内部元素复制到本地list
public CopyOnWriteArrayList(Collection<? extends E> c) {
    Object[] elements;
    if (c.getClass() == CopyOnWriteArrayList.class)
        elements = ((CopyOnWriteArrayList<?>)c).getArray();
    else {
        elements = c.toArray();
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elements.getClass() != Object[].class)
            elements = Arrays.copyOf(elements, elements.length, Object[].class);
    }
    setArray(elements);
}
  • 添加元素
public boolean add(E e) {
    // 获取独占锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 复制array到新数组,添加元素到新数组
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        // 使用新数组替换旧数组
        setArray(newElements);
        return true;
    } finally {
        // 释放锁
        lock.unlock();
    }
}
  • 获取指定位置元素
public E get(int index) {
    return get(getArray(), index);
}
// 获取array数组
final Object[] getArray() {
    return array;
}
// 通过下标访问指定位置的元素
private E get(Object[] a, int index) {
    return (E) a[index];
}
  • 修改指定元素
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();
    }
}
  • 删除元素
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();
    }
}
  • 弱一致迭代器 举例展示弱一致性的迭代器效果
public class CopyArrayDemo {
    private volatile static CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();

    public static void main(String[] args) throws InterruptedException {
        copyOnWriteArrayList.add("hello");
        copyOnWriteArrayList.add("world");
        copyOnWriteArrayList.add("ni");
        copyOnWriteArrayList.add("hao");

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                copyOnWriteArrayList.set(1,"xinao");
                copyOnWriteArrayList.remove(2);
            }
        });

        Iterator<String> iterator = copyOnWriteArrayList.iterator();
        thread.start();
        thread.join();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}