C/C++教程

CopyOnWriteArrayList 的使用与源码分析

本文主要是介绍CopyOnWriteArrayList 的使用与源码分析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

CopyOnWriteArrayList 的使用

优点:

  • CopyOnWriteArrayList 是读写安全的 ArrayList,读操作不加锁,写操作加锁。
  • 写操作时,会复制一份当前数组,然后去添加或移除元素,不会阻塞读操作,故适合读多写少的场景。

缺点:

  • CopyOnWriteArrayList 每次写操作都复制一份数组,如果数组内容过多或者写操作过于频繁,容易发生 GC。
  • CopyOnWriteArrayList 只能保证数据的最终一致性,不能满足实时一致性(读写不互斥)。

代码示例:

@Test
public void testCopyOnWriteArrayList() throws InterruptedException {
    List<Integer> list = IntStream.rangeClosed(1, 20).boxed().collect(Collectors.toList());
    CopyOnWriteArrayList cowList = new CopyOnWriteArrayList(list);

    new Thread(() -> {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println("添加的元素:" + (21));
            cowList.add(21);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }, "t1").start();

    new Thread(() -> {
        try {
            TimeUnit.SECONDS.sleep(1);
            System.out.println("添加的元素:" + (22));
            cowList.add(22);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }, "t2").start();

    for (int i = 0, size = cowList.size(); i < size; i++) {
        System.out.println("cowListSize:" + (size = cowList.size()));
        System.out.println("value:" + cowList.get(i));
        TimeUnit.MILLISECONDS.sleep(500);
    }

    TimeUnit.SECONDS.sleep(10);

    /**
         * 执行的结果:
         *
         * cowListSize:20
         * value:1
         * cowListSize:20
         * value:2
         * 添加的元素:22
         * cowListSize:21
         * value:3
         * cowListSize:21
         * value:4
         * 添加的元素:21
         * cowListSize:22
         * value:5
         * cowListSize:22
         * value:6
         * cowListSize:22
         * value:7
         * cowListSize:22
         * value:8
         * cowListSize:22
         * value:9
         * cowListSize:22
         * value:10
         * cowListSize:22
         * value:11
         * cowListSize:22
         * value:12
         * cowListSize:22
         * value:13
         * cowListSize:22
         * value:14
         * cowListSize:22
         * value:15
         * cowListSize:22
         * value:16
         * cowListSize:22
         * value:17
         * cowListSize:22
         * value:18
         * cowListSize:22
         * value:19
         * cowListSize:22
         * value:20
         * cowListSize:22
         * value:22
         * cowListSize:22
         * value:21
         */
}

源码分析

数据结构

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

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

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

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

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

    // 默认构造器,初始化一个空的数组
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }

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

size

public int size() {
    // 默认是空数组,不为 null
    return getArray().length;
}

isEmpty

public boolean isEmpty() {
    return size() == 0;
}

eq

private static boolean eq(Object o1, Object o2) {
    return (o1 == null) ? o2 == null : o1.equals(o2);
}

indexOf

private static int indexOf(Object o, Object[] elements, int index, int fence) {
    // 如果 o 是 null,则在数组 [index, fence) 区间中找出第一个为 null 的元素的位置
    if (o == null) {
        for (int i = index; i < fence; i++)
            if (elements[i] == null)
                return i;
    } else {
        // o 不为 null,则遍历找出 o 在数组中的位置
        for (int i = index; i < fence; i++)
            if (o.equals(elements[i]))
                return i;
    }
    // 元素 o 在数组中不存在,返回 -1
    return -1;
}

lastIndexOf

// 从 index 位置开始向前找等于 o 的元素位置
private static int lastIndexOf(Object o, Object[] elements, int index) {
    if (o == null) {
        for (int i = index; i >= 0; i--)
            if (elements[i] == null)
                return i;
    } else {
        for (int i = index; i >= 0; i--)
            if (o.equals(elements[i]))
                return i;
    }
    return -1;
}

contains

public boolean contains(Object o) {
    Object[] elements = getArray();
    return indexOf(o, elements, 0, elements.length) >= 0;
}

indexOf

public int indexOf(Object o) {
    Object[] elements = getArray();
    return indexOf(o, elements, 0, elements.length);
}

indexOf

public int indexOf(E e, int index) {
    Object[] elements = getArray();
    return indexOf(e, elements, index, elements.length);
}

lastIndexOf

public int lastIndexOf(Object o) {
    Object[] elements = getArray();
    return lastIndexOf(o, elements, elements.length - 1);
}

clone

public Object clone() {
    try {
        @SuppressWarnings("unchecked")
        CopyOnWriteArrayList<E> clone =
            (CopyOnWriteArrayList<E>) super.clone();
        clone.resetLock();
        return clone;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError();
    }
}

toArray

public Object[] toArray() {
    Object[] elements = getArray();
    return Arrays.copyOf(elements, elements.length);
}

get

private E get(Object[] a, int index) {
    return (E) a[index];
}

get

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

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 {
            // 保证 volatile 写语义
            // Not quite a no-op; ensures volatile write semantics
            setArray(elements);
        }
        return oldValue;
    } finally {
        lock.unlock();
    }

    /**
        JDK 11 中,源码变化如下:

        public E set(int index, E element) {
            synchronized (lock) {
                Object[] es = getArray();
                E oldValue = elementAt(es, index);

                if (oldValue != element) {
                    es = es.clone();
                    es[index] = element;
                }
                // Ensure volatile write semantics even when oldvalue == element
                setArray(es);
                return oldValue;
            }
        }
        */
}

add

// 添加元素
// 先复制一个数组(Arrays.copyOf)
// 再添加元素
// 覆盖旧数组(setArray)
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();
    }

    /**
        JDK 11 中,源码变化如下:

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

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

remove

// 移除元素
// 将原数组移除指定元素,再复制一个新的数组(Arrays.copyOf)
// 覆盖旧数组(setArray)
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();
    }
}

iterator

public Iterator<E> iterator() {
    return new COWIterator<E>(getArray(), 0);
}

// 迭代器类
static final class COWIterator<E> implements ListIterator<E> {
    // 数组
    private final Object[] snapshot;
    // 当前遍历的数组下标
    private int cursor;

    private COWIterator(Object[] elements, int initialCursor) {
        cursor = initialCursor;
        snapshot = elements;
    }

    // 是否还有元素可迭代
    public boolean hasNext() {
        // 如果当前遍历的数组下标小于数组长度,则表示还可以继续迭代
        return cursor < snapshot.length;
    }

    // 当前遍历的数组下标 cursor 前是否已经遍历过元素
    public boolean hasPrevious() {
        return cursor > 0;
    }

    // 获取当前数组下标 cursor 上的元素
    @SuppressWarnings("unchecked")
    public E next() {
        if (! hasNext())
            throw new NoSuchElementException();
        return (E) snapshot[cursor++];
    }

    @SuppressWarnings("unchecked")
    public E previous() {
        if (! hasPrevious())
            throw new NoSuchElementException();
        return (E) snapshot[--cursor];
    }

    public int nextIndex() {
        return cursor;
    }

    public int previousIndex() {
        return cursor-1;
    }

    /**
         * Not supported. Always throws UnsupportedOperationException.
         * @throws UnsupportedOperationException always; {@code remove}
         *         is not supported by this iterator.
         */
    public void remove() {
        throw new UnsupportedOperationException();
    }

    /**
         * Not supported. Always throws UnsupportedOperationException.
         * @throws UnsupportedOperationException always; {@code set}
         *         is not supported by this iterator.
         */
    public void set(E e) {
        throw new UnsupportedOperationException();
    }

    /**
         * Not supported. Always throws UnsupportedOperationException.
         * @throws UnsupportedOperationException always; {@code add}
         *         is not supported by this iterator.
         */
    public void add(E e) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        Object[] elements = snapshot;
        final int size = elements.length;
        for (int i = cursor; i < size; i++) {
            @SuppressWarnings("unchecked") E e = (E) elements[i];
            action.accept(e);
        }
        cursor = size;
    }
}
这篇关于CopyOnWriteArrayList 的使用与源码分析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!