前言

今天我们一起学习下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。如果在遍历的过程中有其他线程尝试改变数组的内容,就会拷贝一份新的数据进行变更,而后面再来访问这个数组的线程,看到的就是变更过的数组。

  1. 创建一个CopyOnWriteArrayList数组numbers;

    CopyOnWriteArrayList<Integer> numbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 78});

  2. 创建一个遍历器iterator;

    Iterator<Integer> iterator = numbers.iterator();

  3. 给numbers中增加(或删除、修改)一个元素;

    numbers.add(100);

  4. 利用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,新老版本分离,保证读的高性能,适用于以读为主的情况。

参考资料

  1. Guide to CopyOnWriteArrayList
  2. CopyOnWriteArrayList详解
  3. 官方文档:CopyOnWriteArrayList

本号专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。

Java并发-CopyOnWriteArrayList的更多相关文章

  1. Java并发编程:并发容器之CopyOnWriteArrayList(转载)

    Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...

  2. Java并发编程:并发容器之CopyOnWriteArrayList

    转载: Java并发编程:并发容器之CopyOnWriteArrayList Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个 ...

  3. 【转】Java并发编程:并发容器之CopyOnWriteArrayList

    Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改, ...

  4. Java并发编程原理与实战三十四:并发容器CopyOnWriteArrayList原理与使用

    1.ArrayList的实现原理是怎样的呢? ------>例如:ArrayList本质是实现了一个可变长度的数组. 假如这个数组的长度为10,调用add方法的时候,下标会移动到下一位,当移动到 ...

  5. 11、Java并发编程:并发容器之CopyOnWriteArrayList

    Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...

  6. Java并发指南14:Java并发容器ConcurrentSkipListMap与CopyOnWriteArrayList

    原文出处http://cmsblogs.com/ 『chenssy』 到目前为止,我们在Java世界里看到了两种实现key-value的数据结构:Hash.TreeMap,这两种数据结构各自都有着优缺 ...

  7. JAVA并发编程J.U.C学习总结

    前言 学习了一段时间J.U.C,打算做个小结,个人感觉总结还是非常重要,要不然总感觉知识点零零散散的. 有错误也欢迎指正,大家共同进步: 另外,转载请注明链接,写篇文章不容易啊,http://www. ...

  8. Java并发集合的实现原理

    本文简要介绍Java并发编程方面常用的类和集合,并介绍下其实现原理. AtomicInteger 可以用原子方式更新int值.类 AtomicBoolean.AtomicInteger.AtomicL ...

  9. java并发容器类

    本文主要介绍java并发容器相关实现类,collections节点下接口方法介绍. Queue Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是Blocking ...

随机推荐

  1. php身份证号的验证

    //身份证号验证 03 protected function checkIdCard(){ 04 if(empty($_POST['idcard'])){ 05 return false; 06 } ...

  2. spark streaming 接收kafka消息之二 -- 运行在driver端的receiver

    先从源码来深入理解一下 DirectKafkaInputDStream 的将 kafka 作为输入流时,如何确保 exactly-once 语义. val stream: InputDStream[( ...

  3. 『 效率工具 』Spring Boot版的轻量级代码生成器,减少70%以上的开发任务

    一. 前言 之前很着迷于代码自动生成,减少写重复代码的工作量.网络上也搜索了很久,有基于插件的,有GUI的.但其配置和学习成本都比较高,都不是很如我意. 本想自己用SpringBoot写一个,在收集相 ...

  4. 前后端分离时代,Java 程序员的变与不变!

    事情的起因是这样的,有个星球的小伙伴向邀请松哥在知乎上回答一个问题,原题是: 前后端分离的时代,Java后台程序员的技术建议? 松哥认真看了下这个问题,感觉对于初次接触前后端分离的小伙伴来说,可能都会 ...

  5. Django ORM基础篇【转载】

    ORM( Object relational mapping 对象关系映射)D:把面向对象中的类和数据库表一一对应起来,在django项目与数据库之间起着桥梁的                     ...

  6. 推荐三个学习git的网站或教程

    廖雪峰官方教程:https://www.liaoxuefeng.com/wiki/896043488029600/900388704535136 ProGit中文版:https://git-scm.c ...

  7. Redis 在java中的使用(登录验证,5分钟内连续输错3次密码,锁住帐号,半小时后解封)(三)

    在java中使用redis,做简单的登录帐号的验证,使用string类型,使用redis的过期时间功能 1.首先进行redis的jar包的引用,因为用的是springBoot,springBoot集成 ...

  8. c# 开发ActiveX控件,添加事件,QT调用事件

    c# 开发 ActiveX 的过程参考我的另一篇文章 :  https://www.cnblogs.com/baqifanye/p/10414004.html 本篇讲如何 在C# 开发的ActiveX ...

  9. 通过phpmyadmin设置数据库密码后若出现phpmyadmin拒绝访问的情况

    方法一:可以修改config.inc.php配置文件中的$cfg['Servers'][$i]['password'] = '你的密码'; 方法二:将config.inc.php配置文件中的$cfg[ ...

  10. json数据转为对象,一般在前台把数据传回后端中使用 转https://www.cnblogs.com/zxtceq/p/6610214.html

    public static JArray GetData2JArray(string url, string key) { string jsonData = HttpHelper.HttpGet(u ...