CopyOnWriteArrayList源码解析(1)
此文已由作者赵计刚授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
注:在看这篇文章之前,如果对ArrayList底层不清楚的话,建议先去看看ArrayList源码解析。
http://www.cnblogs.com/java-zhao/p/5102342.html
1、对于CopyOnWriteArrayList需要掌握以下几点
创建:CopyOnWriteArrayList()
添加元素:即add(E)方法
获取单个对象:即get(int)方法
删除对象:即remove(E)方法
遍历所有对象:即iterator(),在实际中更常用的是增强型的for循环去做遍历
注:CopyOnWriteArrayList是一个线程安全,读操作时无锁的ArrayList。
2、创建
public CopyOnWriteArrayList()
使用方法:
List<String> list = new CopyOnWriteArrayList<String>();
相关源代码:
private volatile transient Object[] array;//底层数据结构 /**
* 获取array
*/
final Object[] getArray() {
return array;
} /**
* 设置Object[]
*/
final void setArray(Object[] a) {
array = a;
} /**
* 创建一个CopyOnWriteArrayList
* 注意:创建了一个0个元素的数组
*/
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
注意点:
设置一个容量为0的Object[];ArrayList会创造一个容量为10的Object[]
3、添加元素
public boolean add(E e)
使用方法:
list.add("hello");
源代码:
/**
* 在数组末尾添加元素
* 1)获取锁
* 2)上锁
* 3)获取旧数组及其长度
* 4)创建新数组,容量为旧数组长度+1,将旧数组拷贝到新数组
* 5)将要增加的元素加入到新数组的末尾,设置全局array为新数组
*/
public boolean add(E e) {
final ReentrantLock lock = this.lock;//这里为什么不直接用this.lock(即类中已经初始化好的锁)去上锁
lock.lock();//上锁
try {
Object[] elements = getArray();//获取当前的数组
int len = elements.length;//获取当前数组元素
/*
* Arrays.copyOf(elements, len + 1)的大致执行流程:
* 1)创建新数组,容量为len+1,
* 2)将旧数组elements拷贝到新数组,
* 3)返回新数组
*/
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;//新数组的末尾元素设成e
setArray(newElements);//设置全局array为新数组
return true;
} finally {
lock.unlock();//解锁
}
}
注意点:
Arrays.copyOf(T[] original, int newLength)该方法在ArrayList中讲解过
疑问:
在add(E)方法中,为什么要重新定义一个ReentrantLock,而不直接使用那个定义的类变量锁(全局锁)
答:事实上,按照他那样写,即使是在add、remove、set中存在多个引用,最后也是一个实例this.lock,所以不管你在add、remove、set中怎样去从新定义一个ReentrantLock,其实add、remove、set中最后使用的都是同一个锁this.lock,也就是说,同一时刻,add/remove/set只能有一个在运行。这样讲,就是说,下边这段代码完全可以做一个修改。修改前的代码:
public boolean add(E e) {
final ReentrantLock lock = this.lock;//这里为什么不直接用this.lock(即类中已经初始化好的锁)去上锁
lock.lock();//上锁修改后的代码:
public boolean add(E e) {
//final ReentrantLock lock = this.lock;//这里为什么不直接用this.lock(即类中已经初始化好的锁)去上锁
this.lock.lock();//上锁根据以上代码可知,每增加一个新元素,都要进行一次数组的复制消耗,那为什么每次不将数组的元素设大(比如说像ArrayList那样,设置为原来的1.5倍+1),这样就会大大减少因为数组元素复制所带来的消耗?
4、获取元素
public E get(int index)
使用方法:
list.get(0)
源代码:
/**
* 根据下标获取元素
* 1)获取数组array
* 2)根据索引获取元素
*/
public E get(int index) {
return (E) (getArray()[index]);
}
注意点:
获取不需要加锁
疑问:在《分布式Java应用:基础与实践》一书中作者指出:读操作会发生脏读,为什么?
从类属性部分,我们可以看到array数组是volatile修饰的,也就是当你对volatile进行写操作后,会将写过后的array数组强制刷新到主内存,在读操作中,当你读出数组(即getArray())时,会强制从主内存将array读到工作内存,所以应该不会发生脏读才对呀!!!
补:volatile的介绍见《附2 volatile》,链接如下:
http://www.cnblogs.com/java-zhao/p/5125698.html
免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 10本最热门科普书免费送!人工智能数学物理获奖经典佳作!
CopyOnWriteArrayList源码解析(1)的更多相关文章
- ArrayList、CopyOnWriteArrayList源码解析(JDK1.8)
本篇文章主要是学习后的知识记录,存在不足,或许不够深入,还请谅解. 目录 ArrayList源码解析 ArrayList中的变量 ArrayList构造函数 ArrayList中的add方法 Arra ...
- CopyOnWriteArrayList源码解析
Java并发包提供了很多线程安全的集合,有了他们的存在,使得我们在多线程开发下,可以和单线程一样去编写代码,大大简化了多线程开发的难度,但是如果不知道其中的原理,可能会引发意想不到的问题,所以知道其中 ...
- 第三章 CopyOnWriteArrayList源码解析
注:在看这篇文章之前,如果对ArrayList底层不清楚的话,建议先去看看ArrayList源码解析. http://www.cnblogs.com/java-zhao/p/5102342.html ...
- CopyOnWriteArrayList源码解析(2)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 5.删除元素 public boolean remove(Object o) 使用方法: list.remo ...
- CopyOnWriteArraySet源码解析
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 注:在看这篇文章之前,如果对CopyOnWriteArrayList底层不清楚的话,建议先去看看CopyOn ...
- 第四章 CopyOnWriteArraySet源码解析
注:在看这篇文章之前,如果对CopyOnWriteArrayList底层不清楚的话,建议先去看看CopyOnWriteArrayList源码解析. http://www.cnblogs.com/jav ...
- EventBus源码解析 源码阅读记录
EventBus源码阅读记录 repo地址: greenrobot/EventBus EventBus的构造 双重加锁的单例. static volatile EventBus defaultInst ...
- EventBus3.0源码解析
本文主要介绍EventBus3.0的源码 EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递. EventBus使用简单,并将事件发布和订阅充 ...
- Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例
概要 上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解Arra ...
随机推荐
- spring学习 五 依赖注入的方式
依赖注入有两种方式: 1 构造注入,如果<bean>标签下使用<contructor-arg>,则是构造注入 2 setter注入,就是调用setter方法注入,如果<b ...
- 2018.10.27 洛谷P2915奶牛混合起来Mixed Up Cows(状压dp)
传送门 状压dp入门题. 按照题意建一个图. 要求的就是合法的链的总数. 直接f[i][j]f[i][j]f[i][j]表示当前状态为jjj,下一位要跟iii连起来的方案数. 然后从没被选并且跟iii ...
- PHP实现图片批量压缩
set_time_limit(0); global $source_dir; global $target_dir; $source_dir = "D:/images/";//目标 ...
- git安装项目步骤
1.git clone git@gitee(github).com:项目地址.git 2.cd 项目根目录 3.composer install 4.如果需要数据迁移,cmd中到项目根目录 php a ...
- CProgressCtrl进度条控件实现进度滚动效果
关于CProgressCtrl 控件的基本操作网上有很多资料,可我想实现进度条中进度滚动效果,即很多时候程序出现的等待或启动画面,如下图: 实现这个效果的函数为SetMarquee(_In_ BOOL ...
- error C4430: error 2141
c:\evan\workspace\1\1\netwowkippack.h(50) : error C2146: 语法错误 : 缺少“;”(在标识符“nSourPort”的前面) c:\evan\wo ...
- PHP函数gmstrftime()将秒数转换成天时分秒
http://yangjunwei.com/a/930.html PHP函数gmstrftime()将秒数转换成天时分秒 一个应用场景需要用到倒计时的时分秒,比如新浪微博授权有效期剩余: 7天16 ...
- s4-6 二层交换
为什么需要二层交换? 有很多LAN,如何将它们连接起来? 可用网桥(bridges )将它们连接起来. 网桥工作在DLL层,通过检查MAC地址做出转发帧的决策 不会检查网络层,所以,IPv ...
- CentOS7 安装可视化脚本安装包Webmin
一.简介 Webmin是一个基于Web的Linux系统管理界面.你就可以通过图形化的方式设置用户账号.Apache.DNS.文件共享等服务. 二.安装 1.下载安装包到本地Windows系统 http ...
- java学习路线图(2018年最新版)
最近有些网友问我如何自学 Java 后端,还有些是想从别的方向想转过来,但都不太了解 Java 后端究竟需要学什么,究竟要从哪里学起,哪些是主流的 Java 后端技术等等,导致想学,但又很迷茫,不知从 ...