我是靠谱客的博主 友好舞蹈,这篇文章主要介绍Java并发-CopyOnWriteArrayList前言CopyOnWriteArrayList API例子1:插入(删除)数据的同时进行遍历例子2:不支持一边遍历一边删除结论参考资料,现在分享给大家,希望可以做个参考。

前言

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

CopyOnWriteArrayList API

CopyOnWriteArrayList的定义如下:

复制代码
1
2
3
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;

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

  2. 创建一个遍历器iterator;

    复制代码
    1
    Iterator<Integer> iterator = numbers.iterator();

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

    复制代码
    1
    numbers.add(100);

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

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

完整的例子如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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();,例子代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。
javaadu

最后

以上就是友好舞蹈最近收集整理的关于Java并发-CopyOnWriteArrayList前言CopyOnWriteArrayList API例子1:插入(删除)数据的同时进行遍历例子2:不支持一边遍历一边删除结论参考资料的全部内容,更多相关Java并发-CopyOnWriteArrayList前言CopyOnWriteArrayList内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部