首页 > 代码库 > CopyOnWriteArrayList源码解析

CopyOnWriteArrayList源码解析

 

 

CopyOnWriteArrayListjava1.5版本提供的一个线程安全的ArrayList变体。

在讲解5.1.1ArrayList的时候,有说明ArrayListfail-fast特性,它是指在遍历过程中,如果ArrayList内容发生过修改,会抛出ConcurrentModificationException

在多线程环境下,这种情况变得尤为突出,参见测试代码(代码基于java8):

 

ArrayList<Integer> list = new ArrayList<>();ExecutorService executorService = Executors.newFixedThreadPool(10);for (int i = 0; i < 100; i++) {// 启动一个写ArrayList的线程executorService.execute(() -> {list.add(1);});// 启动一个读ArrayList的线程executorService.execute(() -> {for (Integer v : list) {System.out.println(v);}});}

 

 

 

设想下面两种处理方式:

1、不使用迭代器形式转而使用下标来遍历,这就带来了一个问题,读写没有分离,写操作会影响到读的准确性,甚至导致IndexOutOfBoundsException,比如下面的例子:

上述例子在多线程执行过程中,list.remove(0)会减少listsize,而读操作使用的是首次遍历的size,必然会出现严重的运行时异常,所以,遍历下标的方法不可取。

 

              executorService.execute(() -> {          list.remove(0);        });        executorService.execute(() -> {            for (int x = 0, len = list.size(); x < len; x++) {              System.out.println(list.get(x));            }        });

 

2、不直接遍历list,而是把list拷贝一份数组,再行遍历,比如把读过程修改成下面这样:

executorService.execute(() -> {    for (Integer v : list.toArray(new Integer[0])) {        System.out.println(v);    }});

 

 

此方法在CopyOnWriteArrayList出现之前较为常见,其本质是把list内容拷贝到了一个新的数组中,CopyOnWriteArrayList也是采取的类似的手段,区别在于,这个例子使用的是CopyOnRead方式,也就是读时拷贝

下面来介绍下CopyOnWriteArrayList写时拷贝的实现方式。

1.1.1.1.1 写时拷贝

写时拷贝,自然是在做写操作时,把原始数据拷贝到一个新的数组,涉及到写操作的是三个方法addremoveset,以add方法为例:

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

 

 

可以注意到,在每一次add操作里,数组都被copy了一份副本,这就是写时拷贝的原理。

那么,写时拷贝和读时拷贝各有什么优势呢?

如果一个list的遍历操作比写入操作频繁,应该使用CopyOnWriteArrayList,反之,则考虑使用读时拷贝的方式

CopyOnWriteArrayList源码解析