资讯专栏INFORMATION COLUMN

Java并发-CopyOnWriteArrayList

Meathill / 2280人阅读

摘要:前言今天我们一起学习下并发包里的工具类。的定义如下它也属于集合框架的一部分,是的线程安全的变体,跟的不同在于针对数组的修改操作等是基于内部拷贝的一份数据而进行的。发生修改时候做,新老版本分离,保证读的高性能,适用于以读为主的情况。

前言

今天我们一起学习下java.util.concurrent并发包里的CopyOnWriteArrayList工具类。当有多个线程可能同时遍历、修改某个公共数组时候,如果不希望因使用synchronize关键字锁住整个数组而影响性能,可以考虑使用CopyOnWriteArrayList。

CopyOnWriteArrayList API

CopyOnWriteArrayList的定义如下:

</>复制代码

  1. public class CopyOnWriteArrayList
  2. extends Object
  3. implements List, RandomAccess, Cloneable, Serializable

它也属于Java集合框架的一部分,是[ArrayList]()的线程安全的变体,跟ArrayList的不同在于:CopyOnWriteArrayList针对数组的修改操作(add、set等)是基于内部拷贝的一份数据而进行的。换句话说,即使在一个线程进行遍历操作时有其他线程可能进行插入或删除操作,我们也可以“线程安全”得遍历CopyOnWriteArrayList。

例子1:插入(删除)数据的同时进行遍历

CopyOnWriteArrayList的实现原理是,在一个线程开始遍历(创建Iterator对象)时,内部会创建一个“快照”数组,遍历基于这个快照Iterator进行,在遍历过程中这个快照数组不会改变,也就不会抛出ConcurrentModificationException。如果在遍历的过程中有其他线程尝试改变数组的内容,就会拷贝一份新的数据进行变更,而后面再来访问这个数组的线程,看到的就是变更过的数组。

创建一个CopyOnWriteArrayList数组numbers;

</>复制代码

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

创建一个遍历器iterator;

</>复制代码

  1. Iterator iterator = numbers.iterator();

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

</>复制代码

  1. numbers.add(100);

利用iterator遍历数组的元素,发现遍历的结果是Iterator对象创建之前的;

</>复制代码

  1. List result = new LinkedList<>();
  2. iterator.forEachRemaining(result::add);
  3. assertThat(result).containsOnly(1, 3, 5, 78);

完整的例子如下:

</>复制代码

  1. package org.java.learn.concurrent.copyonwritearraylist;
  2. import java.util.Iterator;
  3. import java.util.LinkedList;
  4. import java.util.List;
  5. import java.util.concurrent.CopyOnWriteArrayList;
  6. import static org.assertj.core.api.Assertions.*;
  7. /**
  8. * 作用:
  9. * User: duqi
  10. * Date: 2017/11/9
  11. * Time: 11:20
  12. */
  13. public class CopyOnWriteArrayListExample {
  14. public static void main(String[] args) {
  15. CopyOnWriteArrayList numbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 78});
  16. Iterator iterator = numbers.iterator();
  17. numbers.add(100);
  18. List result = new LinkedList<>();
  19. iterator.forEachRemaining(result::add);
  20. assertThat(result).containsOnly(1, 3, 5, 78);
  21. Iterator iterator2 = numbers.iterator();
  22. numbers.remove(3);
  23. List result2 = new LinkedList<>();
  24. iterator2.forEachRemaining(result2::add);
  25. assertThat(result2).containsOnly(1, 3, 5, 78, 100);
  26. }
  27. }
例子2:不支持一边遍历一边删除

由于CopyOnWriteArrayList的实现机制——>修改操作和读操作拿到的Iterator对象指向的不是一个数组,因此不支持基于Iterator对象的方法结果的删除:public void remove();,例子代码如下:

</>复制代码

  1. package org.java.learn.concurrent.copyonwritearraylist;
  2. import java.util.Iterator;
  3. import java.util.concurrent.CopyOnWriteArrayList;
  4. /**
  5. * 作用: User: duqi Date: 2017/11/9 Time: 13:40
  6. */
  7. public class CopyOnWriteArrayListExample2 {
  8. public static void main(String[] args) {
  9. try {
  10. testExceptionThrow();
  11. } catch (Exception e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. private static void testExceptionThrow() {
  16. CopyOnWriteArrayList numbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 78});
  17. Iterator integerIterator = numbers.iterator();
  18. while (integerIterator.hasNext()) {
  19. integerIterator.remove();
  20. }
  21. }
  22. }
结论

CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。发生修改时候做copy,新老版本分离,保证读的高性能,适用于以读为主的情况。

参考资料

Guide to CopyOnWriteArrayList

CopyOnWriteArrayList详解

官方文档:CopyOnWriteArrayList

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75532.html

相关文章

  • Java 线程相关类

    摘要:提供了线程安全的共享对象,在编写多线程代码时,可把不安全的整个变量封装进,或者把该对象与线程相关的状态使用保存并不能替代同步机制,两者面向的问题领域不同。 ThreadLocal类 使用ThreadLocal类可以简化多线程编程时的并发访问,使用这个工具类可以很简捷地隔离多线程程序的竞争资源。Java5之后,为ThreadLocal类增加了泛型支持,即ThreadLocal Threa...

    Sanchi 评论0 收藏0
  • [Java并发-11] 并发容器的使用

    摘要:同步容器及其注意事项中的容器主要可以分为四个大类,分别是和,但并不是所有的容器都是线程安全的。并发容器及其注意事项在版本之前所谓的线程安全的容器,主要指的就是同步容器,当然因为所有方法都用来保证互斥,串行度太高了,性能太差了。 Java 并发包有很大一部分内容都是关于并发容器的,因此学习和搞懂这部分的内容很有必要。 Java 1.5 之前提供的同步容器虽然也能保证线程安全,但是性能很差...

    legendaryedu 评论0 收藏0
  • CopyOnWriteArrayList你都不知道,怎么拿offer?

    摘要:今天主要讲解的是本文力求简单讲清每个知识点,希望大家看完能有所收获一和回顾线程安全的和我们知道是用于替代的,是线程安全的容器。使用迭代器遍历时不需要显示加锁,看看与方法的实现可能就有点眉目了。 前言 只有光头才能变强 showImg(https://segmentfault.com/img/remote/1460000016931828?w=1120&h=640); 前一阵子写过一篇C...

    noONE 评论0 收藏0
  • 带你了解集合世界的fail-fast机制 和 CopyOnWriteArrayList 源码详解

    摘要:体现的就是适配器模式。数组对象集合世界中的机制机制集合世界中比较常见的错误检测机制,防止在对集合进行遍历过程当中,出现意料之外的修改,会通过异常暴力的反应出来。而在增强循环中,集合遍历是通过进行的。 前言 学习情况记录 时间:week 2 SMART子目标 :Java 容器 记录在学习Java容器 知识点中,关于List的重点知识点。 知识点概览: 容器中的设计模式 从Array...

    young.li 评论0 收藏0
  • java并发编程学习12--并发数据结构简介

    摘要:并发数据结构存在的理由串行数据结构在并发环境下是不安全的,而直接使用锁又会带来性能的影响,所以专门设计了针对并发环境下的数据结构,其中使用了无锁运算来保证性能。在高并发的情况下过多的锁操作会拖累系统的性能。是由数组结构和数组结构组成。 【并发数据结构存在的理由 串行数据结构在并发环境下是不安全的,而直接使用锁又会带来性能的影响,所以jdk专门设计了针对并发环境下的数据结构,其中使用了无...

    dreamGong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<