欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

CopyOnWriteArrayList源码分析

程序员文章站 2022-07-14 16:09:47
...

前两天刚分析完了ArrayList,趁热打铁,接着来看CopyOnWriteArrayList。
CopyOnWriteArrayList与ArrayList大部分的实现都是类似,只是在ArrayList的基础上加上了一个锁对象,对所有改变集合数据结构的操作加了同步代码块,现在的synchronized不再是之前那个稳定不变的mutex_lock重量级锁,而是借助mark_word等,在并发情况逐步升级锁,单线程情况就算加了synchronized关键字,性能就跟没写差不多。

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 8673264195747942595L;

    final transient Object lock = new Object();

    private transient volatile Object[] elements;

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

    final void setArray(Object[] a) {
        elements = a;
    }


    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }

CopyOnWriteArrayList最大的特点就是"写时复制"。集合结构发生改变是先获取object 的锁,复制出一个新的数组,在新数组上进行操作,之后把新数组赋值给集合的数据字段,释放锁。 可以想到高并发的情况下,多个线程同时修改结构的操作是要竞争锁,但是同时的读取数据就没问题。实现的是读写分离,读在老数据上,写在新数据上,最后新数据数组覆盖老的。
读操作无须加锁,

   // Positional Access Operations
    @SuppressWarnings("unchecked")
    private E get(Object[] a, int index) {
        return (E) a[index];
    }

    static String outOfBounds(int index, int size) {
        return "Index: " + index + ", Size: " + size;
    }

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

写操作在新数组上操作,setArray 覆盖老数组。

 public E set(int index, E element) {
        synchronized (lock) {
            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;
        }
    }

    public boolean add(E e) {
        synchronized (lock) {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        }
    }

    final void setArray(Object[] a) {
        elements = a;
    }

Arrays.copyOf 生成一个新的数组,并且复制原数据。


  public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }



  public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }