前言

今天我们一起学习下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. Delphi的Anymouse方法探秘

    匿名函数是用Interface来实现的,具体细节可以看http://www.raysoftware.cn/?p=38匿名函数还是非常方便的.比如自己封装的异步调用.Async(procedure(AP ...

  2. QT---Winsocket获取网关(Gateway) 主机IP等信息

      基于WinPcap库做开发,需要利用到局域网的默认网关地址和Mac地址,但是WinPcap实现获取网关IP地址没有很好的思路,可以知道的是网关的接收和发出的数据包数量一般是比局域网内的各主机要多的 ...

  3. 一个拼图工具TImageBox的制作思路

    http://www.cnblogs.com/del/archive/2010/04/24/1719631.html

  4. DNS查询命令

    dig(domain information groper)是一个在类Unix命令行模式下查询DNS,包括NS记录,A记录,MX记录等相关信息的工具 一.简单介绍使用dig命令查询DNS的方法 dig ...

  5. 使用 python 提取照片中的手机信息

    使用 python 提取照片中的手机信息 最近在做一个项目,有一个很重要的点是需要获取使用用户的手机信息,这里我选择从照片中获取信息.有人会问为什么不从手机里面直接获取设备信息.由于现在android ...

  6. 10 jQuery的事件绑定和解绑

    1.绑定事件 语法: bind(type,data,fn) 描述:为每一个匹配元素的特定事件(像click)绑定一个事件处理器函数. 参数解释: type (String) : 事件类型 data ( ...

  7. 拉格朗日乘子法 - KKT条件 - 对偶问题

    接下来准备写支持向量机,然而支持向量机和其他算法相比牵涉较多的数学知识,其中首当其冲的就是标题中的拉格朗日乘子法.KKT条件和对偶问题,所以本篇先作个铺垫. 大部分机器学习算法最后都可归结为最优化问题 ...

  8. win的cmd环境中设置***代理

    想在win的cmd环境中设置代理进行FQ安装软件,如npm等一系列. 1.配置好shadowsocks,然后编辑服务器,查看代理端口 2.打开win命令行cmd set http_proxy=http ...

  9. python读取excel文件中所有sheet表格

    sales: store: """(1)用load_workbook函数打开excel文件,返回一个工作簿对象 (2)用工作簿对象获取所有的sheet (3)第一个for ...

  10. 02_javaSE面试题:单例设计模式

    还记得很多年前,面试就让在白板上写个单例模式,当时技术渣渣,还写的是class A.面试官还说,你就不能写个Singleton. 面试题 编程题:写一个Singleton示例 解析 什么是Single ...