zoukankan      html  css  js  c++  java
  • CopyOnWriteArrayList

    CopyOnWriteArrayList

    四个关注点

    关 注 点结 论
    CopyOnWriteArrayList是否允许空 允许
    CopyOnWriteArrayList是否允许重复数据 允许
    CopyOnWriteArrayList是否有序 有序
    CopyOnWriteArrayList是否线程安全 线程安全

    首先提两点:

    1、CopyOnWriteArrayList位于java.util.concurrent包下,可想而知,这个类是为并发而设计的

    2、CopyOnWriteArrayList,顾名思义,Write的时候总是要Copy,也就是说对于CopyOnWriteArrayList,任何可变的操作(add、set、remove等等)都是伴随复制这个动作的,后面会解读CopyOnWriteArrayList的底层实现机制

    对于CopyOnWriteArrayList来说,增加、删除、修改、插入的原理都是一样的,话不多说,用源码和图进行演示:

    1 public static void main(String[] args)
    2 {
    3     List<Integer> list = new CopyOnWriteArrayList<Integer>();
    4     list.add(1);
    5     list.add(2);
    6 }

    源码

    public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
       Object[] elements = getArray();
       int len = elements.length;
       Object[] newElements = Arrays.copyOf(elements, len + 1);
       newElements[len] = e;
       setArray(newElements);
       return true;
    } finally {
       lock.unlock();
    }
    }

    每一步都清楚地表示在图上了,一次add大致经历了几个步骤:

    1、加锁

    2、拿到原数组,得到新数组的大小(原数组大小+1),实例化出一个新的数组来

    3、把原数组的元素复制到新数组中去

    4、新数组最后一个位置设置为待添加的元素(因为新数组的大小是按照原数组大小+1来的)

    5、把Object array引用指向新数组

    6、解锁

    整个过程看起来比较像ArrayList的扩容。有了这个基础,我们再来看一下第5行的add了一个整数2做了什么,这应该非常简单了,还是画一张图来表示:

    另外,插入、删除、修改操作也都是一样,每一次的操作都是以对Object[] array进行一次复制为基础的,如果上面的流程看懂了,那么研究插入、删除、修改的源代码应该不难。

    为什么要用CopyOnWriteArrayList而不用其他的?

    这个就和 原子性一致性 有关,ArrayList 和 linkedList 首先线程不安全就不满足,还有一种vector ,虽然说线程是安全的,但是不是绝对的安全,只是在单独的增、删、改时可以,但是在组合的时候就不行,所以这里应用了CopyOnWriteArrayList,说下它的优缺点:

    缺点:安全性高那效率肯定就不高,修改代价十分昂贵,每次修改都伴随着一次的数组复制;但同时优点也十分明显,就是在并发下不会产生任何的线程安全问题,也就是绝对的线程安全,这也是为什么我们要使用CopyOnWriteArrayList的原因。

    优点:

    (1)读写分离

    我们读取CopyOnWriteArrayList的时候读取的是CopyOnWriteArrayList中的Object[] array,但是修改的时候,操作的是一个新的Object[] array,读和写操作的不是同一个对象,这就是读写分离。这种技术数据库用的非常多,在高并发下为了缓解数据库的压力,即使做了缓存也要对数据库做读写分离,读的时候使用读库,写的时候使用写库,然后读库、写库之间进行一定的同步,这样就避免同一个库上读、写的IO操作太多

    (2)最终一致

    对CopyOnWriteArrayList来说,线程1读取集合里面的数据,未必是最新的数据。因为线程2、线程3、线程4四个线程都修改了CopyOnWriteArrayList里面的数据,但是线程1拿到的还是最老的那个Object[] array,新添加进去的数据并没有,所以线程1读取的内容未必准确。不过这些数据虽然对于线程1是不一致的,但是对于之后的线程一定是一致的,它们拿到的Object[] array一定是三个线程都操作完毕之后的Object array[],这就是最终一致。最终一致对于分布式系统也非常重要,它通过容忍一定时间的数据不一致,提升整个分布式系统的可用性与分区容错性。当然,最终一致并不是任何场景都适用的,像火车站售票这种系统用户对于数据的实时性要求非常非常高,就必须做成强一致性的。

    最后总结一点,随着CopyOnWriteArrayList中元素的增加,CopyOnWriteArrayList的修改代价将越来越昂贵,因此,CopyOnWriteArrayList适用于读操作远多于修改操作的并发场景中

  • 相关阅读:
    P5049 旅行(数据加强版)(基环树)
    P5024 保卫王国(动态dp/整体dp/倍增dp)
    CF891C Envy(离线/在线+可撤销并查集/并查集/LCT)
    CF1217题解
    CF1215题解
    浅谈bitset
    CF1214题解
    CF1213F Unstable String Sort(差分)
    C++创建和使用动态链接库
    交叉编译openssl1.1.1a
  • 原文地址:https://www.cnblogs.com/create-and-orange/p/11235746.html
Copyright © 2011-2022 走看看