文档章节

CopyOnWriteArrayList源码剖析

筱虾米
 筱虾米
发布于 06/14 21:06
字数 956
阅读 7
收藏 3
点赞 0
评论 0

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源码分析

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

狂小白
06/09
0
0
浅谈CopyOnWriteArraySet

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

小鱼嘻嘻
2017/10/23
0
0
LinkedList&CopyOnWriteArrayList源码分析

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

MrLeeWen
2017/12/15
0
0
并发编程6:CopyOnWriteArrayList 的写时复制

首先提个问题: 线程安全的 List 集合有什么? CopyOnWriteArrayList 的特点以及使用场景? 如果这个问题你答不上来,那这篇文章可能就对你有些价值。 读完本文你将了解: CopyOnWriteArray...

u011240877
2017/08/20
0
0
JDK容器学习之CopyOnWriteArrayList:线程安全保障机制

JDK容器学习之CopyOnWriteArrayList 列表容器常见的有ArrayList和LinkedList,然而两者都是非线程安全的,若应用场景对线程安全有需求,则可以使用CopyOnWriteArrayList来代替传统的Vector ...

孟飞阳
2017/10/20
0
0
CopyOnWriteArrayList详解

CopyOnWriteArrayList是ArrayList 的一个线程安全的变体,其中所有可变操作(add、set等等)都是通过对底层数组进行一次新的复制来实现的。 这一般需要很大的开销,但是当遍历操作的数量大大...

古月楼
2013/10/08
0
7
【追光者系列】HikariCP源码分析之ConcurrentBag

摘自【工匠小猪猪的技术世界】 1.这是一个系列,有兴趣的朋友可以持续关注 2.如果你有HikariCP使用上的问题,可以给我留言,我们一起沟通讨论 3.希望大家可以提供我一些案例,我也希望可以支...

查查
05/02
0
0
JDK容器学习之CopyOnWriteArrayList:线程安全保障机制

JDK容器学习之CopyOnWriteArrayList 列表容器常见的有 和,然而两者都是非线程安全的,若应用场景对线程安全有需求,则可以使用来代替传统的 I. 存储结构 先看下类中定义的成员变量, 一个数组...

小灰灰Blog
2017/10/20
0
5
并发容器之写时拷贝的 List 和 Set

对于一个对象来说,我们为了保证它的并发性,通常会选择使用声明式加锁方式交由我们的 Java 虚拟机来完成自动的加锁和释放锁的操作,例如我们的 synchronized。也会选择使用显式锁机制来主动...

Single_YAM
2017/12/02
0
0
源码|并发一枝花之CopyOnWriteArrayList

CopyOnWriteArrayList的设计思想非常简单,但在设计层面有一些小问题需要注意。 JDK版本:oracle java 1.8.0_102 本来不想写的,但是github上CopyOnWriteArrayList的code results也有165k,为...

猴子007
2017/11/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

自定义OkHttp的UA

背景 上次的问题很明显 由于我们的ua清一色OkHttp导致快速定位到内部应用。 既然如此我们是否考虑可以在UA上做点手脚。 自定义我们的UA呢??? 分析 首先UA在 均为okhttp/3.2.0 大概率是由于...

Mr_Qi
16分钟前
0
0
【scikit-learn】01:使用案例对sklearn库进行简单介绍

sklearn学习笔记:Quick Start 源地址:http://scikit-learn.org/stable/tutorial/basic/tutorial.html # -*-coding:utf-8-*-''' Author:kevinelstri Datetime:2017.2.16'''......

wangxuwei
20分钟前
0
0
Linux Kernel 4.16 系列停止维护,用户应升级至 4.17

知名 Linux 内核维护人员兼开发人员 Greg Kroah-Hartman 近日在发布 4.16.18 版本的同时,宣布这是 4.16 系列的最后一个维护版本,强烈建议用户立即升级至 4.17 系列。 Linux 4.16 于 2018 年...

问题终结者
21分钟前
0
0
Apache配置时.htaccess失效不起作用的原因分析

.htaccess 失效的原因 1. 重写规则有问题,检查自己的重写规则 2.Apache配置问题,配置中没有配置启用 rewrite a2enmod rewrite 3.网站配置文件没有启用配置需要配置 000-default.conf <Dire...

TU-DESGIN
42分钟前
1
0
两个求最大公约数C/C++算法实现

#include<stdio.h> #include<time.h> #include <iostream>using namespace std;//求最大公约数 LCD(Largest Common Division)//短除法 //m=8251, n=6105; int LCD_ShortDiv(int m, ......

失落的艺术
47分钟前
1
0
QueryPerformanceCounter

windows的Sleep函数,睡眠线程指定毫秒数,可以用来做毫秒延时。 对于微秒延时,没有一个现成的函数,但是可以通过 QueryPerformanceFrequency QueryPerformanceCounter 来间接实现。原理就是...

开飞色
今天
1
0
log4j2使用AsyncRoot不显示行号问题处理

<AsyncRoot level="info" includeLocation="true"> <AppenderRef ref="File"/></AsyncRoot><!--1.异步logger,还需要在pom.xml中添加disruptor的依赖。2.includeLocation结合异......

小翔
今天
3
0
安卓手机上 K 歌,声音延迟怎么解决?

这篇文章可以为你提供一个解决录音和播放同步问题的思路,而且解决了声音从手机传输到耳机上有延时的问题。 初识音频 在开始之前,我先简单介绍一下音频相关的基础知识,方便下文理解。 我们...

编辑部的故事
今天
2
0
使用token实现在有效期内APP自动登录功能

使用token实现在有效期内APP自动登录功能 http://sevennight.cc/2016/07/19/auto_login_impl.html

风云海滩
今天
2
0
Spring Boot集成RabbitMQ发送接收JSON

默认情况下RabbitMQ发送的消息是转换为字节码,这里介绍一下如何发送JSON数据。 ObjectMapper 最简单发送JSON数据的方式是把对象使用ObjectMapper等JSON工具类把对象转换为JSON格式,然后发送...

小致dad
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部