CopyOnWriteArrayList源码解析

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

在讲解5.1.1ArrayList的时候,有说明ArrayList的fail-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)会减少list的size,而读操作使用的是首次遍历的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 写时拷贝

写时拷贝,自然是在做写操作时,把原始数据拷贝到一个新的数组,涉及到写操作的是三个方法add,remove和set,以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,反之,则考虑使用读时拷贝的方式

时间: 2024-08-26 17:31:51

CopyOnWriteArrayList源码解析的相关文章

第三章 CopyOnWriteArrayList源码解析

注:在看这篇文章之前,如果对ArrayList底层不清楚的话,建议先去看看ArrayList源码解析. http://www.cnblogs.com/java-zhao/p/5102342.html 1.对于CopyOnWriteArrayList需要掌握以下几点 创建:CopyOnWriteArrayList() 添加元素:即add(E)方法 获取单个对象:即get(int)方法 删除对象:即remove(E)方法 遍历所有对象:即iterator(),在实际中更常用的是增强型的for循环去做

第四章 CopyOnWriteArraySet源码解析

注:在看这篇文章之前,如果对CopyOnWriteArrayList底层不清楚的话,建议先去看看CopyOnWriteArrayList源码解析. http://www.cnblogs.com/java-zhao/p/5121944.html 1.对于CopyOnWriteArraySet需要掌握以下几点 创建:CopyOnWriteArraySet() 添加元素:即add(E)方法 删除对象:即remove(E)方法 遍历所有对象:即iterator(),在实际中更常用的是增强型的for循环去

Java并发编程与技术内幕:CopyOnWriteArrayList、CopyOnWriteArraySet源码解析

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文主要讲了Java中CopyOnWriteArrayList .CopyOnWriteArraySet的源码分析 一.CopyOnWriteArrayList源码分析 CopyOnWriteArrayList在java的并发场景中用得其实并不是非常多,因为它并不能完全保证读取数据的正确性.其主要有以下的一些特点:1.适合场景读多写少2.不能保证读取数据一定是正确 的,因为get时是不

Android EventBus源码解析, 带你深入理解EventBus

上一篇带大家初步了解了EventBus的使用方式,详见:Android EventBus实战 没听过你就out了,本篇博客将解析EventBus的源码,相信能够让大家深入理解该框架的实现,也能解决很多在使用中的疑问:为什么可以这么做?为什么这么做不好呢? 1.概述 一般使用EventBus的组件类,类似下面这种方式: [java] view plain copy public class SampleComponent extends Fragment { @Override public vo

Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例

概要 上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解ArrayList.先对ArrayList有个整体认识,再学习它的源码,最后再通过例子来学习如何使用它.内容包括:第1部分 ArrayList简介第2部分 ArrayList数据结构第3部分 ArrayList源码解析(基于JDK1.6.0_45)第4部分 ArrayList遍历方式第5部分 toArray

Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例

java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例 概要 上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解ArrayList.先对ArrayLis

【转】Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例

概要 上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解ArrayList.先对ArrayList有个整体认识,再学习它的源码,最后再通过例子来学习如何使用它.内容包括:第1部分 ArrayList简介第2部分 ArrayList数据结构第3部分 ArrayList源码解析(基于JDK1.6.0_45)第4部分 ArrayList遍历方式第5部分 toArray

Android EventBus3.0使用及源码解析

叨了个叨 最近因为换工作的一些琐事搞的我一个头两个大,也没怎么去学新东西,实在是有些愧疚.新项目用到了EventBus3.0,原来只是听说EventBus的鼎鼎大名,一直没仔细研究过.趁着周末有些时间,研究下代码,也算没有虚度光阴. EventBus GitHub : https://github.com/greenrobot/EventBus EventBus3.0简介 EventBus是greenrobot出品的一个用于Android中事件发布/订阅的库.以前传递对象可能通过接口.广播.文件

第六章 ReentrantLock源码解析2--释放锁unlock()

最常用的方式: int a = 12; //注意:通常情况下,这个会设置成一个类变量,比如说Segement中的段锁与copyOnWriteArrayList中的全局锁 final ReentrantLock lock = new ReentrantLock(); lock.lock();//获取锁 try { a++;//业务逻辑 } catch (Exception e) { }finally{ lock.unlock();//释放锁 } 注:关于lock()方法的源码解析,请参照"第五章