(转)线程安全的CopyOnWriteArrayList介绍
转载自:线程安全的CopyOnWriteArrayList介绍
证明CopyOnWriteArrayList是线程安全的
先写一段代码证明CopyOnWriteArrayList确实是线程安全的。
运行上面的代码,没有报出
java.util.ConcurrentModificationException
1
说明了CopyOnWriteArrayList并发多线程的环境下,仍然能很好的工作。
CopyOnWriteArrayList如何做到线程安全的
CopyOnWriteArrayList使用了一种叫写时复制的方法,当有新元素添加到CopyOnWriteArrayList时,先从原有的数组中拷贝一份出来,然后在新的数组做写操作,写完之后,再将原来的数组引用指向到新数组。
当有新元素加入的时候,如下图,创建新数组,并往新数组中加入一个新元素,这个时候,array这个引用仍然是指向原数组的。
当元素在新数组添加成功后,将array这个引用指向新数组。
CopyOnWriteArrayList的整个add操作都是在锁的保护下进行的。
这样做是为了避免在多线程并发add的时候,复制出多个副本出来,把数据搞乱了,导致最终的数组数据不是我们期望的。
CopyOnWriteArrayList的add操作的源代码如下:
publicbooleanadd(E e) {//1、先加锁final ReentrantLocklock=this.lock;lock.lock();try{ Object[] elements = getArray();intlen = elements.length;//2、拷贝数组Object[] newElements = Arrays.copyOf(elements, len +1);//3、将元素加入到新数组中newElements[len] = e;//4、将array引用指向到新数组setArray(newElements);returntrue; }finally{//5、解锁lock.unlock(); }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
由于所有的写操作都是在新数组进行的,这个时候如果有线程并发的写,则通过锁来控制,如果有线程并发的读,则分几种情况:
1、如果写操作未完成,那么直接读取原数组的数据;
2、如果写操作完成,但是引用还未指向新数组,那么也是读取原数组数据;
3、如果写操作完成,并且引用已经指向了新的数组,那么直接从新数组中读取数据。
可见,CopyOnWriteArrayList的读操作是可以不用加锁的。
CopyOnWriteArrayList的使用场景
通过上面的分析,CopyOnWriteArrayList 有几个缺点:
1、由于写操作的时候,需要拷贝数组,会消耗内存,如果原数组的内容比较多的情况下,可能导致young gc或者full gc
2、不能用于实时读的场景,像拷贝数组、新增元素都需要时间,所以调用一个set操作后,读取到数据可能还是旧的,虽然CopyOnWriteArrayList 能做到最终一致性,但是还是没法满足实时性要求;
CopyOnWriteArrayList 合适读多写少的场景,不过这类慎用
因为谁也没法保证CopyOnWriteArrayList 到底要放置多少数据,万一数据稍微有点多,每次add/set都要重新复制数组,这个代价实在太高昂了。在高性能的互联网应用中,这种操作分分钟引起故障。
CopyOnWriteArrayList透露的思想
如上面的分析CopyOnWriteArrayList表达的一些思想:
1、读写分离,读和写分开
2、最终一致性
3、使用另外开辟空间的思路,来解决并发冲突
参考的文章
JAVA中的COPYONWRITE容器
(转)线程安全的CopyOnWriteArrayList介绍相关推荐
- 线程安全的CopyOnWriteArrayList介绍
证明CopyOnWriteArrayList是线程安全的 先写一段代码证明CopyOnWriteArrayList确实是线程安全的. ReadThread.java import java.util. ...
- Java自带的线程池ThreadPoolExecutor详细介绍说明和实例运用
Java 5 开始,Java 提供了自己的线程池.线程池就是一个线程的容器,每次只执行额定数量的线程. java.util.concurrent.ThreadPoolExecutor 就是这样的线程池 ...
- 创建线程:pthread_creat() 函数介绍
创建线程:pthread_creat() 函数介绍 语法 : int pthread_creat(pthread_t *thread,const pthread_attr_t *attr,void * ...
- JDK1.8源码分析:线程安全的CopyOnWriteArrayList与CopyOnWriteArraySet
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者:服务端开发 blog.csdn.net/u01001 ...
- java 线程简介_java多线程介绍
java多线程介绍 多线程的基本实现 进程指运行中的程序,每个进程都会分配一个内存空间,一个进程中存在多个线程,启动一个JAVA虚拟机,就是打开个一个进程,一个进程有多个线程,当多个线程同时进行,就叫 ...
- 进程与线程的关系简单介绍
文章目录 1. 进程与线程的简单介绍 2. 进程 基本特点: 3. 线程 1. 进程与线程的简单介绍 什么是进程?打个比方,你的程序要执行,操作系统就会杷你的exe文件加载到内存中.那就生成一个进 ...
- 线程安全的CopyOnWriteArrayList集合
CopyOnWriteArrayList 读取和写入源码简单分析(java.util.concurrent包下) 1. CopyOnWriteArrayList 读取操作的实现 读取操作没有任何同步控 ...
- 线程本地ThreadLocal的介绍与使用!
ThreadLocal简介 我们通过上两篇的学习,我们已经知道了变量值的共享可以使用public static变量的形式,所有的线程都使用同一个被public static修饰的变量. 那么如果我们想 ...
- 自定义线程池-线程池工作流程介绍
ThreadPoolExecutor参数详解 我们可以通过下面的场景理解ThreadPoolExecutor中的各个参数; a客户(任务)去银行(线程池)办理业务,但银行刚开始营业,窗口服务员还未就位 ...
最新文章
- php tp5路由教程,附件八 TP5路由设置源码
- hurst代码 python_python数据类型—字符串
- python爬虫系列之数据的存储(二):csv库的使用
- Ivan and Powers of Two CodeForces - 305C(set)
- Mongodb 分片与副本集
- 案例 体检套餐管理系统 需求一览 c# 1614099219
- android.mk 编译jar包,Androidmk 使用详解
- JavaScript中执行环境和栈
- Navicat MySQL连接Linux下MySQL的及2003错误解决方案
- java输出各国人的问候语_世界各国问候语及发音.doc
- 如何在正则表达式中使用变量?
- IDEA 导入项目 导入不进去
- andorid studio 无法识别app项目解决
- 网络带宽单位转换 — MB/s、Mb/s、Mbps、Mbit/s、Kbps
- 树莓派/linux 功能配置(含网络)不定期更新
- java interop,服务器程序的Xamarin-Java.Interop体验(一)
- 3.6 Meterpreter 键盘记录
- 使用电子签章确立区块链电子发票的法律效力
- 四、微信小程序之简单计算器(学习记录)
- (C语言)猜数字游戏,猜中负数,直接游戏结束,一次猜中,bingo,1~3次,Lucky you!......