CopyOnWriteArrayList源码解析(2)
此文已由作者赵计刚授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
5、删除元素
public boolean remove(Object o)
使用方法:
list.remove("hello")
源代码:
/**
* 删除list中的第一个o
* 1)获取锁、上锁
* 2)获取旧数组、旧数组的长度len
* 3)如果旧数组长度为0,返回false
* 4)如果旧数组有值,创建新数组,容量为len-1
* 5)从0开始遍历数组中除了最后一个元素的所有元素
* 5.1)将旧数组中将被删除元素之前的元素复制到新数组中,
* 5.2)将旧数组中将被删除元素之后的元素复制到新数组中
* 5.3)将新数组赋给全局array
* 6)如果是旧数组的最后一个元素要被删除,则
* 6.1)将旧数组中将被删除元素之前的元素复制到新数组中
* 6.2)将新数组赋给全局array
*/
public boolean remove(Object o) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();//获取原数组
int len = elements.length;//获取原数组长度
if (len != 0) {//如果有数据
// Copy while searching for element to remove
// This wins in the normal case of element being present
int newlen = len - 1;//新数组长度为原数组长度-1
Object[] newElements = new Object[newlen];//创建新数组 for (int i = 0; i < newlen; ++i) {//遍历新数组(不包含最后一个元素)
if (eq(o, elements[i])) {
// 将旧数组中将被删除元素之后的元素复制到新数组中
for (int k = i + 1; k < len; ++k)
newElements[k - 1] = elements[k];
setArray(newElements);//将新数组赋给全局array
return true;
} else
newElements[i] = elements[i];//将旧数组中将被删除元素之前的元素复制到新数组中
} if (eq(o, elements[newlen])) {//将要删除的元素时旧数组中的最后一个元素
setArray(newElements);
return true;
}
}
return false;
} finally {
lock.unlock();
}
}
判断两个对象是否相等:
/**
* 判断o1与o2是否相等
*/
private static boolean eq(Object o1, Object o2) {
return (o1 == null ? o2 == null : o1.equals(o2));
}
注意点:
需要加锁
ArrayList的remove使用了System.arraycopy(这是一个native方法),而这里没使用,所以理论上这里的remove的性能要比ArrayList的remove要低
6、遍历所有元素
iterator() hasNext() next()
使用方法:
讲解用的:
Iterator<String> itr = list.iterator();
while(itr.hasNext()){
System.out.println(itr.next());
}
实际中使用的:
for(String str : list){
System.out.println(str);
}
源代码:
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
private static class COWIterator<E> implements ListIterator<E> {
private final Object[] snapshot;//数组快照
private int cursor;//可看做数组索引
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;//将实际数组赋给数组快照
}
public boolean hasNext() {
return cursor < snapshot.length;//0~snapshot.length-1
}
public E next() {
if (!hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
说明:这一块儿代码非常简单,看看代码注释就好。
注意:
由于遍历的只是全局数组的一个副本,即使全局数组发生了增删改变化,副本也不会变化,所以不会发生并发异常。但是,可能在遍历的过程中读到一些刚刚被删除的对象。
注意点:
总结:
线程安全,读操作时无锁的ArrayList
底层数据结构是一个Object[],初始容量为0,之后每增加一个元素,容量+1,数组复制一遍
增删改上锁、读不上锁
遍历过程由于遍历的只是全局数组的一个副本,即使全局数组发生了增删改变化,副本也不会变化,所以不会发生并发异常
读多写少且脏数据影响不大的并发情况下,选择CopyOnWriteArrayList
疑问:
每增加一个新元素,都要进行一次数组的复制消耗,那为什么每次不将数组的元素设大(比如说像ArrayList那样,设置为原来的1.5倍+1),这样就会大大减少因为数组元素复制所带来的消耗?
get(int)操作会发生脏读,为什么?
免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 【译文】抽象漏洞法则
CopyOnWriteArrayList源码解析(2)的更多相关文章
- ArrayList、CopyOnWriteArrayList源码解析(JDK1.8)
本篇文章主要是学习后的知识记录,存在不足,或许不够深入,还请谅解. 目录 ArrayList源码解析 ArrayList中的变量 ArrayList构造函数 ArrayList中的add方法 Arra ...
- CopyOnWriteArrayList源码解析
Java并发包提供了很多线程安全的集合,有了他们的存在,使得我们在多线程开发下,可以和单线程一样去编写代码,大大简化了多线程开发的难度,但是如果不知道其中的原理,可能会引发意想不到的问题,所以知道其中 ...
- CopyOnWriteArrayList源码解析(1)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 注:在看这篇文章之前,如果对ArrayList底层不清楚的话,建议先去看看ArrayList源码解析. ht ...
- 第三章 CopyOnWriteArrayList源码解析
注:在看这篇文章之前,如果对ArrayList底层不清楚的话,建议先去看看ArrayList源码解析. http://www.cnblogs.com/java-zhao/p/5102342.html ...
- 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 ...
随机推荐
- rbtposeekf的注意事项
1.发布的odom topic以及 imu topic必须加上协方差部分:2.在发布odom的时候,去掉里面的odom->base_link的tf,因为这个tf会在robot_pose_ekf包 ...
- spring jpetstore研究入门(zz)
spring jpetstore研究入门 分类: java2008-12-21 23:25 561人阅读 评论(2) 收藏 举报 springstrutsibatissearchweb框架servle ...
- 核心一:DI
1.DI:中文名称:依赖注入 2.英文名称:(Dependency Injection) 3.DI是什么?? 3.1 DI和IoC是一样的 3.2 当一个类(A)中需要依赖另一类(B)对象时,把B赋值 ...
- 2019.01.21 NOIP训练 ak树(点分治)
传送门 题意简述:给一棵带权树,问在上面随机选两个点距离是4的倍数的概率. 思路: 由于总方案数为定值n2n^2n2,所以只用求总方案数. 这个跟聪聪可可差不多,可以用类似树形dpdpdp的方法边点分 ...
- 2019.01.19 codeforces915E.Physical Education Lessons(ODT)
传送门 ODT水题(当然可以上线段树) 支持区间01覆盖,询问全局1的个数. 思路:直接上ODTODTODT. 不会的点这里 代码: #include<bits/stdc++.h> #de ...
- pyinstaller基本操作
pyinstaller 打包错误http://www.fmwei.com/linux/pyinstaller-lib-error.html 只需要复制python安装目录下的动态库到系统地动态库目录即 ...
- html的那些小小细节
1.get post方式提交的不同 get:数据放在url的后面,用?连接 会在客户端保留缓存信息,不安全 ...
- 模块import,from ..import...
首次导入模块发生3件事 1.创建一个模块的名称空间 2.执行文件spam.py,将执行过程中产生的名字都放到模块的名称空间中 3.在当前执行文件中直接拿到一个名字,该名字就是执行模块中相对应的名字 f ...
- Ng第十一课:机器学习系统的设计(Machine Learning System Design)
11.1 首先要做什么 11.2 误差分析 11.3 类偏斜的误差度量 11.4 查全率和查准率之间的权衡 11.5 机器学习的数据 11.1 首先要做什么 在接下来的视频将谈到机器学习系 ...
- Java Applet小应用
开发和部署方式 嵌入到HTML网页中,用<Applet></Applet>标签识别.java环境用浏览器的,在第一次打开时下载,可开发成以后打开,默认不必再次下载.也可 ...