CopyOnWriteArrayList

  • CopyOnWriteArrayList适合于多线程场景下使用,其采用读写分离的思想,读操作不上锁,写操作上锁,且写操作效率较低。
  • CopyOnWriteArrayList基于fail-safe机制,每次修改都会在原先基础上复制一份,修改完毕后在进行替换
  • CopyOnWriteArrayList采用的是ReentrantLock进行上锁。
  • CopyOnWriteArrayList和ArrayList一样,其底层数据结构也是数组,加上transient不让其被序列化,加上volatile修饰来保证多线程下的其可见性和有序性。
  • CopyOnWriteArrayList效率比ArrayList低不少,毕竟多线程场景下,其每次都是要在原数组基础上复制一份在操作耗内存和时间,而ArrayList只是容量满了进行扩容,因此在非多线程的场景下还是用ArrayList吧。

构造函数

public CopyOnWriteArrayList() {
//默认创建一个大小为0的数组
setArray(new Object[0]);
} final void setArray(Object[] a) {
array = a;
} public CopyOnWriteArrayList(Collection<? extends E> c) {
Object[] elements;
//如果当前集合是CopyOnWriteArrayList的类型的话,直接赋值给它
if (c.getClass() == CopyOnWriteArrayList.class)
elements = ((CopyOnWriteArrayList<?>)c).getArray();
else {
//否则调用toArra()将其转为数组
elements = c.toArray();
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elements.getClass() != Object[].class)
elements = Arrays.copyOf(elements, elements.length, Object[].class);
}
//设置数组
setArray(elements);
} public CopyOnWriteArrayList(E[] toCopyIn) {
//将传进来的数组元素拷贝给当前数组
setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}

读取方法(未加锁

final Object[] getArray() {
return array;
}
public int size() {
return getArray().length;
}
public boolean isEmpty() {
return size() == 0;
}
public int indexOf(E e, int index) {
Object[] elements = getArray();
return indexOf(e, elements, index, elements.length);
}
public int lastIndexOf(Object o) {
Object[] elements = getArray();
return lastIndexOf(o, elements, elements.length - 1);
} ........

add方法(使用ReentrantLock加锁

public boolean add(E e) {
//使用ReentrantLock上锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
//调用getArray()获取原来的数组
Object[] elements = getArray();
int len = elements.length;
//复制老数组,得到一个长度+1的数组
Object[] newElements = Arrays.copyOf(elements, len + 1);
//添加元素,在用setArray()函数替换原数组
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}

可见其修改操作是基于fail-safe机制,像我们的String一样,不在原来的对象上直接进行操作,而是复制一份对其进行修改,另外此处的修改操作是利用Lock锁进行上锁的,所以保证了线程安全问题。

remove方法(使用ReentrantLock加锁

public boolean remove(Object o) {
Object[] snapshot = getArray();
int index = indexOf(o, snapshot, 0, snapshot.length);
return (index < 0) ? false : remove(o, snapshot, index);
} private boolean remove(Object o, Object[] snapshot, int index) {
final ReentrantLock lock = this.lock;
//上锁
lock.lock();
try {
Object[] current = getArray();
int len = current.length;
if (snapshot != current) findIndex: {
int prefix = Math.min(index, len);
for (int i = 0; i < prefix; i++) {
if (current[i] != snapshot[i] && eq(o, current[i])) {
index = i;
break findIndex;
}
}
if (index >= len)
return false;
if (current[index] == o)
break findIndex;
index = indexOf(o, current, index, len);
if (index < 0)
return false;
}
//复制一个数组
Object[] newElements = new Object[len - 1];
System.arraycopy(current, 0, newElements, 0, index);
System.arraycopy(current, index + 1,
newElements, index,
len - index - 1);
//替换原数组
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}

思路与add方法一致。

【多线程】JUC版的CopyOnWriteArrayList的更多相关文章

  1. Java 经典面试题:聊一聊 JUC 下的 CopyOnWriteArrayList

    ArrayList 是我们常用的工具类之一,但是在多线程的情况下,ArrayList 作为共享变量时,并不是线程安全的.主要有以下两个原因: 1. ArrayList 自身的 elementData. ...

  2. 多线程JUC并发篇常见面试详解

    @ 目录 1.JUC 简介 2.线程和进程 3.并非与并行 4.线程的状态 5.wait/sleep的区别 6.Lock 锁(重点) 1.Lock锁 2.公平非公平: 3.ReentrantLock ...

  3. Java多线程-详细版

    基本概念解释 并发:一个处理器处理多个任务,这些任务对于处理器来说是交替运行的,每个时间点只有一个任务在进行. 并行:多个处理器处理多个任务,这些任务是同时运行的.每个时间点有多个任务同时进行. 进程 ...

  4. java多线程系列12 ConcurrentHashMap CopyOnWriteArrayList 简介

    我们知道 ,hashmap 和 arraylist 是线程不安全的 在多线程环境下有数据安全问题, 当然 我们可以通过Collections的一些方法把他们变成线程安全的, Collections.s ...

  5. Reactor模型-多线程程版

    1.概述 在Reactor单线程版本的设计中,I/O任务乃至业务逻辑都由Reactor线程来完成,这无疑增加了Reactor线程的负担,高负载情况下必然会出现性能瓶颈.此外,对于多处理器的服务器来说, ...

  6. java多线程----JUC集合”01之 框架

    java集合的架构.主体内容包括Collection集合和Map类:而Collection集合又可以划分为List(队列)和Set(集合). 1. List的实现类主要有: LinkedList, A ...

  7. Java多线程JUC

    1. volatile 关键字 多线程访问的时候,一个比较严重的问题就是内存不可见,其实在内存访问的时候每一个线程都有一个自己的缓冲区,每次在做修改的时候都是从主存取到数据,然后放到自己的缓冲区中,在 ...

  8. JUC集合之 CopyOnWriteArrayList

    CopyOnWriteArrayList介绍 它相当于线程安全的ArrayList.和ArrayList一样,它是个可变数组:但是和ArrayList不同的时,它具有以下特性: 它最适合于具有以下特征 ...

  9. Java多线程—JUC原子类

    根据修改的数据类型,可以将JUC包中的原子操作类可以分为4类. 1. 基本类型: AtomicInteger, AtomicLong, AtomicBoolean ;2. 数组类型: AtomicIn ...

随机推荐

  1. 前端webpack workflow(二)——Webpack基本使用

    作者:Jogis原文链接:https://github.com/yesvods/Blog/issues/3转载请注明原文链接以及作者信息 前一篇文章介绍了webpack以及安装方法,这次将会介绍web ...

  2. Leetcode1/242/383-HashMap常用方法以及遍历排序方式

    HashMap常用方法以及遍历排序方式 常用方法 map.containsKey() map.put() map1.equals(map2) 遍历方式 Iterator<Map.Entry< ...

  3. CCF201604-2俄罗斯方块

    问题描述 俄罗斯方块是俄罗斯人阿列克谢·帕基特诺夫发明的一款休闲游戏. 游戏在一个15行10列的方格图上进行,方格图上的每一个格子可能已经放置了方块,或者没有放置方块.每一轮,都会有一个新的由4个小方 ...

  4. error: failed to push some refs to 'git@gitee.com:xxxx'

    出现错误的主要原因是向上仓库的一些文件(README.md,LICENSE等文件)不在本地代码目录中 git pull --rebase origin master 通过这行命令, 可以解决 注释福利 ...

  5. 关于webpack,你想知道的都在这;

    咱也标题党一回 哈哈哈 要使用webpack优化项目打包构建速度,首先得知道问题出在哪, 要知道问题出在哪,首先得知道webpack 打包的基本原理才能针对性的去做优化,下面首先了解webpack基本 ...

  6. 面试官:Zookeeper集群怎么搭建?

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 作为一名Java拧螺丝选手,不必 ...

  7. C语言之:结构体动态分配内存(利用结构体数组保存不超过10个学生的信息,每个学生的信息包括:学号、姓名和三门课(高数、物理和英语 )的成绩和平均分(整型)。)

    题目内容: 利用结构体数组保存不超过10个学生的信息,每个学生的信息包括:学号.姓名和三门课(高数.物理和英语 )的成绩和平均分(整型). 编写程序,从键盘输入学生的人数,然后依次输入每个学生的学号. ...

  8. Bugku的exec执行绕过

    题目 思路 1. 打开网页显示403 2. 回去看题目有提示 3. 不用多说,网页访问. 4. 第一行说要传个参数ip,试一下get传参?ip=127.0.0.1 5. 试下 ① 算术运算符 & ...

  9. eBPF Cilium实战(1) - 基于团队的网络隔离

    在 Rainbond 集群中,每个团队对应于底层 Kubernetes 的一个 Namespace ,由于之前使用的底层网络无法进行 Namespace 级别的网络管理,所以在 Rainbond 同一 ...

  10. oracle查看impdp进度

    当数据量比较大的时候,当你导入,导出的时候,在数据库中查看运行的情况,可以利用下面的视图查看: //这里由于导表空间时出现问题,所以再次执行导入任务.但是已有任务在执行,查看执行中的任务方法. ora ...