首页 > 技术文章 > CopyOnWriteArrayList详解

yangfei629 2019-09-16 23:23 原文

1、背景


java诞生之初 就有线程安全的Vector,但Vector对读写都是通过synchronized关键字来同步的,性能并不好

且Vector每次扩容是原来的1倍,存在内存浪费的可能。

对于线程安全的List JDK提供了CopyOnWriteArrayList

 

2、原理


 

2.1 CopyOnWriteArrayList 使用写时复制技术,读写分离

对于写操作,会使用ReentrantLock加锁,对底层数组拷贝一份,进行修改,修改完后再覆盖原来的数组

 

2.2 底层使用数组

private transient volatile Object[] array;

数据使用volatile修饰,故能保证可见性,存在happens-before关系,使得能读到最新的数据

 

2.3 使用ReentrantLock修改

所有的写操作 读会加锁保证线程安全

但读操作是没有任何锁的,故修改过程中的数据,此时另一个线程是读不到的。

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

  

3、迭代


CopyOnWriteArrayList 迭代时安全失败的,不允许在迭代过程中删除元素,如下会抛出异常

/**
         * 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();
        }
View Code

创建迭代时,引用了原数组,故迭代过程中如何修改了CopyOnWriteArrayList,

迭代也不会感知。还是对修改前的数据进行访问(注意这是针对数据元素的增删而言,如何存放对象,对象内部的变化还是有影响的)

static final class COWIterator<E> implements ListIterator<E> {
        /** Snapshot of the array */
        private final Object[] snapshot;
        /** Index of element to be returned by subsequent call to next.  */
        private int cursor;

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

  

 

4、使用场景


缺点:

a. 每次修改都会拷贝数组 占用内存 写性能不高

b.修改的过程中 数据依赖读的是老的数据 ,只能保证最终一致 不能保证强一致性

 

使用场景

1、多线程 读多写少。

2、不要求数据强一致性。

否则推荐使用Collections.synchronziedList


 

推荐阅读