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 ...
随机推荐
- BS架构如何实现即时消息提醒
转载地址:https://blog.csdn.net/cyjch/article/details/51506434
- idea 设置编译的编码方式
Settings->Build,Execution,Deployment->Compiler->Java Compiler,右边的详情列表中,Javac Options有设置参数的地 ...
- Oracle导入的常见语句
登录sql > sqlplus / as sysdba 创建表空间sql > create tablespace TABLESPACE datafile 'e:\tables1.dbf' ...
- 更改Ubuntu默认python版本的两种方法python-> Anaconda
当你安装 Debian Linux 时,安装过程有可能同时为你提供多个可用的 Python 版本,因此系统中会存在多个 Python 的可执行二进制文件.一般Ubuntu默认的Python版本都为2. ...
- abaqus学习笔记-abaqus与umat调用基本原理
参考: 1.http://ivt-abaqusdoc.ivt.ntnu.no:2080/v6.14/books/sub/default.htm 2.ABAQUS 用户材料子程序开发及应用-杨曼娟 3. ...
- AngularJS封装UEditor
<!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8& ...
- Linux(CentOS)下的apache服务器配置与管理
原文链接:http://blog.csdn.net/ylqmf/article/details/5291680 一.WEB服务器与Apache1.web服务器与网址 2.Apache的历史 3.补充h ...
- BeautifulSoup学习心得(一)
[BeautifulSoup最简介] BeautifulSoup,是Python中的一个第三方库,用于帮助解析Html/XML等内容,便于实现后期的内容提取等方面的工作. BeautifulSoup官 ...
- 使用VBA宏批量修改表格
一.批量修改表格,实现所有表格的宽度根据窗口自动调整. Sub test() ' ' test 宏 ' ' Dim i As Table, N As Integer On Error Resume N ...
- 1.8.2suspend与resume方法的缺点-独占
这两个方法使用不当,容易造成公共的同步对象的独占,使得其他线程无法访问公共的同步对象 测试 package com.cky.bean; /** * Created by edison on 2017/ ...