Java并发-CopyOnWriteArrayList
前言
今天我们一起学习下java.util.concurrent并发包里的CopyOnWriteArrayList工具类。当有多个线程可能同时遍历、修改某个公共数组时候,如果不希望因使用synchronize关键字锁住整个数组而影响性能,可以考虑使用CopyOnWriteArrayList。
CopyOnWriteArrayList API
CopyOnWriteArrayList的定义如下:
public class CopyOnWriteArrayList<E>
extends Object
implements List<E>, RandomAccess, Cloneable, Serializable
它也属于Java集合框架的一部分,是ArrayList的线程安全的变体,跟ArrayList的不同在于:CopyOnWriteArrayList针对数组的修改操作(add、set等)是基于内部拷贝的一份数据而进行的。换句话说,即使在一个线程进行遍历操作时有其他线程可能进行插入或删除操作,我们也可以“线程安全”得遍历CopyOnWriteArrayList。
例子1:插入(删除)数据的同时进行遍历
CopyOnWriteArrayList的实现原理是,在一个线程开始遍历(创建Iterator对象)时,内部会创建一个“快照”数组,遍历基于这个快照Iterator进行,在遍历过程中这个快照数组不会改变,也就不会抛出ConcurrentModificationException。如果在遍历的过程中有其他线程尝试改变数组的内容,就会拷贝一份新的数据进行变更,而后面再来访问这个数组的线程,看到的就是变更过的数组。
创建一个CopyOnWriteArrayList数组numbers;
CopyOnWriteArrayList<Integer> numbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 78});
创建一个遍历器iterator;
Iterator<Integer> iterator = numbers.iterator();
给numbers中增加(或删除、修改)一个元素;
numbers.add(100);
利用iterator遍历数组的元素,发现遍历的结果是Iterator对象创建之前的;
List<Integer> result = new LinkedList<>();
iterator.forEachRemaining(result::add);
assertThat(result).containsOnly(1, 3, 5, 78);
完整的例子如下:
package org.java.learn.concurrent.copyonwritearraylist;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import static org.assertj.core.api.Assertions.*;
/**
* 作用:
* User: duqi
* Date: 2017/11/9
* Time: 11:20
*/
public class CopyOnWriteArrayListExample {
public static void main(String[] args) {
CopyOnWriteArrayList<Integer> numbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 78});
Iterator<Integer> iterator = numbers.iterator();
numbers.add(100);
List<Integer> result = new LinkedList<>();
iterator.forEachRemaining(result::add);
assertThat(result).containsOnly(1, 3, 5, 78);
Iterator<Integer> iterator2 = numbers.iterator();
numbers.remove(3);
List<Integer> result2 = new LinkedList<>();
iterator2.forEachRemaining(result2::add);
assertThat(result2).containsOnly(1, 3, 5, 78, 100);
}
}
例子2:不支持一边遍历一边删除
由于CopyOnWriteArrayList的实现机制——>修改操作和读操作拿到的Iterator对象指向的不是一个数组,因此不支持基于Iterator对象的方法结果的删除:public void remove();,例子代码如下:
package org.java.learn.concurrent.copyonwritearraylist;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 作用: User: duqi Date: 2017/11/9 Time: 13:40
*/
public class CopyOnWriteArrayListExample2 {
public static void main(String[] args) {
try {
testExceptionThrow();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void testExceptionThrow() {
CopyOnWriteArrayList<Integer> numbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 78});
Iterator<Integer> integerIterator = numbers.iterator();
while (integerIterator.hasNext()) {
integerIterator.remove();
}
}
}
结论
CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。发生修改时候做copy,新老版本分离,保证读的高性能,适用于以读为主的情况。
参考资料
本号专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。

Java并发-CopyOnWriteArrayList的更多相关文章
- Java并发编程:并发容器之CopyOnWriteArrayList(转载)
Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...
- Java并发编程:并发容器之CopyOnWriteArrayList
转载: Java并发编程:并发容器之CopyOnWriteArrayList Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个 ...
- 【转】Java并发编程:并发容器之CopyOnWriteArrayList
Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改, ...
- Java并发编程原理与实战三十四:并发容器CopyOnWriteArrayList原理与使用
1.ArrayList的实现原理是怎样的呢? ------>例如:ArrayList本质是实现了一个可变长度的数组. 假如这个数组的长度为10,调用add方法的时候,下标会移动到下一位,当移动到 ...
- 11、Java并发编程:并发容器之CopyOnWriteArrayList
Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...
- Java并发指南14:Java并发容器ConcurrentSkipListMap与CopyOnWriteArrayList
原文出处http://cmsblogs.com/ 『chenssy』 到目前为止,我们在Java世界里看到了两种实现key-value的数据结构:Hash.TreeMap,这两种数据结构各自都有着优缺 ...
- JAVA并发编程J.U.C学习总结
前言 学习了一段时间J.U.C,打算做个小结,个人感觉总结还是非常重要,要不然总感觉知识点零零散散的. 有错误也欢迎指正,大家共同进步: 另外,转载请注明链接,写篇文章不容易啊,http://www. ...
- Java并发集合的实现原理
本文简要介绍Java并发编程方面常用的类和集合,并介绍下其实现原理. AtomicInteger 可以用原子方式更新int值.类 AtomicBoolean.AtomicInteger.AtomicL ...
- java并发容器类
本文主要介绍java并发容器相关实现类,collections节点下接口方法介绍. Queue Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是Blocking ...
随机推荐
- 海康威视频监控设备Web查看系统(一):概要篇
声明:本系列文章只提供交流与学习使用.文章中所有涉及到海康威视设备的SDK均可在海康威视官方网站下载得到.文章中所有除官方SDK意外的代码均可随意使用,任何涉及到海康威视公司利益的非正常使用由使用者自 ...
- windows 消息列表
private const UInt32 WM_ACTIVATE = 0x0006; private const UInt32 WM_ACTIVATEAPP = 0x001C; private con ...
- 玩转Java多线程(Lock.Condition的正确使用姿势)
转载请标明博客的地址 本人博客和github账号,如果对你有帮助请在本人github项目AioSocket上点个star,激励作者对社区贡献 个人博客:https://www.cnblogs.com/ ...
- 《Effective Java》-——用私有构造器或者枚举类型强化Singleton属性
Singleton指仅仅被实例化一次的类.Singleton通常被用来代表那些本质上唯一的系统组件,比如窗口管理器或者文件系统.使类成为Singleton会使它的客户端测试变得十分困难,因为无法给Si ...
- MySQL 事务嵌套
MySQL默认自动提交(autocommit=1),如果要开启事务,先关闭自动提交(autocommit=0): InnoDB支持事务,MyISAM不支持: MySQL不支持事务嵌套:已经开启事务后, ...
- Python自学day-12
一.Mysql概述 RDBMS:关系型数据库管理系统.Mysql是一种RDBMS. Oracle:收费 Mysql:Oracle旗下免费 Sqlserver:微软 DB2:IBM Postgresql ...
- Spark学习之路(二)—— Spark开发环境搭建
一.安装Spark 1.1 下载并解压 官方下载地址:http://spark.apache.org/downloads.html ,选择Spark版本和对应的Hadoop版本后再下载: 解压安装包: ...
- .NET开发框架(四)-服务器IIS安装教程
Windows Server 2012 R2 配置篇,包括服务器IIS安装.网络负载均衡器安装.ASP.NET Core 安装. 前三篇教程中,我们分享了框架的功能与视频演示介绍(文尾扫码 加入 框架 ...
- 利用mapWithState实现按照首字母统计的有状态的wordCount
最近在做sparkstreaming整合kafka的时候遇到了一个问题: 可以抽象成这样一个问题:有状态的wordCount,且按照word的第一个字母为key,但是要求输出的格式为(word,1)这 ...
- 趣解 ceph rgw multisite data sync 机制
multisite是ceph rgw对象数据异地容灾备份的一个有效方案,笔者希望深入理解该技术,并应用于生产环境中,然而rgw的这部分代码晦涩难懂,笔者多次尝试阅读,仍云里雾里不解其意,最终流着泪咬着 ...