文档章节

CopyOnWriteArrayList源码剖析

筱虾米
 筱虾米
发布于 06/14 21:06
字数 956
阅读 9
收藏 3

1.1前言

        CopyOnWriteArrayList是JAVA中的并发容器类,同时也是符合写时复制思想的CopyOnWrite容器。写时复制思想即是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

1.2源码分析

1.2.1实现接口

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

    CopyOnWriteArrayList实现了List和RandomAccess接口,使得该容器可以具有列表的基本功能和随机访问的特性,并且实现了Cloneable接口和Serializable接口,表示可被克隆和序列化。

1.2.2成员变量

//重入锁
final transient ReentrantLock lock = new ReentrantLock();

//对象数组,用于存放数据,用volatile修饰
private transient volatile Object[] array;

1.2.3构造函数

//设置数组
final void setArray(Object[] a) {
    array = a;
}

//调用setArray,创建一个空的列表
public CopyOnWriteArrayList() {
    setArray(new Object[0]);
}

//创建一个包含collection的列表
public CopyOnWriteArrayList(Collection<? extends E> c) {
    Object[] elements;
    //判断集合C的类型是否是CopyOnWriteArrayList,如果是则获取集合的数组,否则进入else
    if (c.getClass() == CopyOnWriteArrayList.class)
        elements = ((CopyOnWriteArrayList<?>)c).getArray();
    else {
        elements = c.toArray();//将集合转为数组
        //判断elements的类型是否为Object[]类型,如果不是则转为Object[]类型
        if (elements.getClass() != Object[].class)
            elements = Arrays.copyOf(elements, elements.length, Object[].class);
    }
    setArray(elements);//设置数组
}
//将toCopyIn转为Object[]类型,然后设置数组
public CopyOnWriteArrayList(E[] toCopyIn) {
    setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}

1.2.4get方法

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

public E get(int index) {
    return get(getArray(), index);
}
final Object[] getArray() {
    return array;
}

        get方法没有加锁也没有cas操作,因此代码非常简单。

1.2.5add方法

//将指定元素添加到列表尾部
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;//添加元素e
        setArray(newElements);//设置数组
        return true;
    } finally {
        lock.unlock();//释放锁
    }
}

1.2.6set方法

//替换列表指定位置的元素
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 {
            // Not quite a no-op; ensures volatile write semantics
            setArray(elements);
        }
        return oldValue;//返回旧值
    } finally {
        lock.unlock();//释放锁
    }
}

1.2.7remove方法

//删除指定位置的元素
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));//移动个数为0则表示移除的是数组的最后一个元素,复制elements数组,复制长度为length-1,然后设置数组
        else {//移动个数不为0
            Object[] newElements = new Object[len - 1];//创建一个新数组
            System.arraycopy(elements, 0, newElements, 0, index);//复制index之前的元素
            System.arraycopy(elements, index + 1, newElements, index,
                             numMoved);//复制index之后的元素
            setArray(newElements);//设置数组
        }
        return oldValue;
    } finally {
        lock.unlock();
    }
}

1.3缺点

  • 内存占用问题,有可能造成频繁的垃圾回收。
  • 数据一致性问题,CopyOnWriteArrayList只能保证数据的最终一致性,不能保证数据的实时一致性。

1.4总结

        对于CopyOnWriteArrayList容器来说,只适合读多写少的并发场景下使用。

© 著作权归作者所有

共有 人打赏支持
筱虾米
粉丝 18
博文 29
码字总数 37089
作品 0
杭州
私信 提问
Java多线程系列之线程安全集合CopyOnWriteArrayList

一、CopyOnWriteArrayList介绍 CopyOnWriteArrayList在Java中通常作为ArrayList的线程安全实现,他继承自List并实现了RandomAccess、Cloneable、java.io.Serializable在支持所有List操作的基...

老韭菜
07/31
0
0
CopyOnWriteArrayList你都不知道,怎么拿offer?

只有光头才能变强 前一阵子写过一篇COW(Copy On Write)文章,结果阅读量很低啊...COW奶牛!Copy On Write机制了解一下 可能大家对这个技术比较陌生吧,但这项技术是挺多应用场景的。除了上文...

Java3y
11/07
0
0
浅谈CopyOnWriteArraySet

CopyOnWriteArraySet结构图 CopyOnWriteArraySet.png CopyOnWriteArraySet主要方法 public boolean add(E e); public boolean remove(Object o); CopyOnWriteArraySet解读主要方法 来看一下p......

小鱼嘻嘻
2017/10/23
0
0
Java并发编程之CopyOnWriteArrayList源码分析

并发包中并发List只有CopyOnWriteArrayList这一个,CopyOnWriteArrayList是一个线程安全的ArrayList,对其进行修改操作和元素迭代操作都是在底层创建一个拷贝数组(快照)上进行的,也就是写时...

狂小白
06/09
0
0
LinkedList&CopyOnWriteArrayList源码分析

前言 由于LinkedList和CopyOnWriteArrayList的源码相对来说比较简单,就放在一起分析了。 LinkedList 就从下面一个例子开始分析 首先看默认构造函数: 再看add()方法 这里需要注意下Node,这就...

MrLeeWen
2017/12/15
0
0

没有更多内容

加载失败,请刷新页面

加载更多

详解如何实现一个简单的 vuex

首先我们需要知道为何要使用 vuex。父子组件通信用 prop 和自定义事件可以搞定,简单的非父子组件通信用 bus(一个空的 Vue 实例)。那么使用 vuex 就是为了解决复杂的非父子组件通信。 仅仅...

嫣然丫丫丫
6分钟前
0
0
算法——RangePartitioner实现之reservoirSample

简介 reservoir的作用是:**在不知道文件总行数的情况下,如何从文件中随机的抽取一行?**即是说如果最后发现文字档共有N行,则每一行被抽取的概率均为1/N? 我们可以:定义取出的行号为cho...

freeli
9分钟前
0
0
Python安装及netcdf数据读写

一、在CentOS7系统上安装Python3 在anaconda官网下载(http://https://www.anaconda.com/download/#linux)(Anaconda指的是一个开源的Python发行版本,是Python的包管理器和环境管理器) 下...

voole
13分钟前
0
0
基于NEO的私链(Private Blockchain)

1.准备工作 1.NEO-GUI 2.NEO-CLI 3..NET Core Runtime (不能是2.x版本,官方建议是1.12,实际上我用1.14也是没有问题的) 4.四台windows操作系统的虚拟机(本文是基于AWS的,理论上本地跑虚...

NEO-FANS
19分钟前
0
0
linux中shell if 判断总结

UNIX Shell 里面比较字符写法 -eq 等于; -ne 不等于; -gt 大于; -lt 小于 ; -le 小于等于; -ge 大于等于; -z 空串; -n 非空串; = 两个字符相等; != 两个字符不等 无论什么编程语言都离不开条...

linuxprobe16
34分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部