Java并发-CopyOnWriteArrayList

前言

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

10年积累的成都做网站、成都网站设计经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站制作后付款的网站建设流程,更有青山湖免费网站建设让你可以放心的选择与我们合作。

CopyOnWriteArrayList API

CopyOnWriteArrayList的定义如下:

public class CopyOnWriteArrayList
extends Object
implements List, RandomAccess, Cloneable, Serializable

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

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

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

  1. 创建一个CopyOnWriteArrayList数组numbers;

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

  2. 创建一个遍历器iterator;

    Iterator iterator = numbers.iterator();

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

    numbers.add(100);

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

    List 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 numbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 78});

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

        Iterator iterator2 = numbers.iterator();
        numbers.remove(3);
        List 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 numbers = new CopyOnWriteArrayList<>(new Integer[]{1, 3, 5, 78});
        Iterator integerIterator = numbers.iterator();
        while (integerIterator.hasNext()) {
            integerIterator.remove();
        }
    }
}

结论

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

参考资料

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

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


标题名称:Java并发-CopyOnWriteArrayList
分享网址:http://azwzsj.com/article/ipihcs.html