zoukankan      html  css  js  c++  java
  • CopyOnWriteArrayList 入门及介绍

    本文详细介绍CopyOnWriteArrayList,从概述、原理、使用、源码、缺点等方面进行说明

    背景

      简要介绍下CopyOnWriteArrayList产生的背景

      1.Vedtor 和SynchronizedList的锁力度比较大,基本上可以认为都是加锁在方法层面,并发度降低。(只有一把锁)

      2.CopyOnWriteArrayList降低了锁的力度,并且在迭代时是可以编辑的。

      3.CopyOnWrite容器中其他的实现,如:CopyOnWriteArraySet

    适用场景

      1.读操作需要足够快,写操作慢一点没啥关系;比如系统中黑名单、监听器(监听很多的时间,读的场景多)

    读写规则

      我们都知道读写锁的工作原理,即为多读一写。

      为了将读的性能提高到最大,CopyOnWrite不再加读锁,只提供写写的互斥操作。

    实例代码

      首先我们演示ArrayList在迭代的时候进行了新增或删除,这里我们给出结论:ArrayList在迭代的时候不允许修改,见下代码

      

    package com.yang.concurrent;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    
    public class ArrayListIteratorDemo {
        public static void main(String[] args) {
            ArrayList<Integer> list=new ArrayList<>();
            list.add(1);
            list.add(2);
            list.add(3);
            list.add(4);
            Iterator<Integer> iterator=list.iterator();
            while (iterator.hasNext()){
                Integer item=iterator.next();
                System.out.println(item);
                if (item==2){
                    list.remove(2);
                }
    
            }
        }
    }
    

      运行结果如下:不允许对迭代过程中对List进行操作。

      

      我们用CopyOnWriteArrayList在迭代的时候进行下删除获取新增。

       

    package com.yang.concurrent;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.concurrent.CopyOnWriteArrayList;
    
    public class CopyOnWriteArrayListIteratorDemo {
        public static void main(String[] args) {
            CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
            list.add(1);
            list.add(2);
            list.add(3);
            list.add(4);
            Iterator<Integer> iterator = list.iterator();
            while (iterator.hasNext()) {
                Integer item = iterator.next();
                System.out.println(item);
                if (item == 2) {
                    list.remove(2);
                }
                System.out.println(list);
            }
        }
    }
    

      运行结果如下,允许在迭代的过程中进行对List操作,不影响迭代。如下图。

      

     原理

      我们看下CopyOnWrite的原理。我们用的时候,重新拷贝一份新的,用完之后,将指针指向原来的地址。

      创建新的副本,读写分离

      以下代码演示迭代过程中数据过期的问题,见下代码。

      

    package com.yang.concurrent;
    
    import java.util.Iterator;
    import java.util.concurrent.CopyOnWriteArrayList;
    
    /**
     *本实例演示迭代过程中数据过期的问题
     */
    public class CopyOnWriteArrayListDemo2 {
        public static void main(String[] args) {
            CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>(new Integer[]{1, 2, 3});
            //迭代的数据取决了迭代器的生成时间
            Iterator<Integer> iterator1 = list.iterator();
            list.add(4);
            Iterator<Integer> iterator2 = list.iterator();
            iterator1.forEachRemaining(System.out::println);
            iterator2.forEachRemaining(System.out::println);
        }
    }
    

    CopyOnWriteArrayList缺点

      1.不能保证实时数据一致性

      2.内存占用问题,写操作时复制的,会有内存开销

     源码分析

      我们从初始化、锁、写操作、读操作进行分析。

      初始化代码如下所示:

      

       观察下使用的Lock锁。

      

    public class CopyOnWriteArrayList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
        private static final long serialVersionUID = 8673264195747942595L;
    
        /** The lock protecting all mutators */
        final transient ReentrantLock lock = new ReentrantLock();
    
        /** The array, accessed only via getArray/setArray. */
        private transient volatile Object[] array;
    .........
    

       读操作的实现代码如下,我们发现未加锁,则读操作可能获取的数据不是最新的。

        

     @SuppressWarnings("unchecked")
        private E get(Object[] a, int index) {
            return (E) a[index];
        }
    

      我们重点分析下写操作,我们发现写的时候,是互斥的,需要获取ReentrantLock,对原来的数组进行拷贝,待写入完成后,重新改变引用地址。如下所示:

      

        public E set(int index, E element) {
            //获取锁
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                Object[] elements = getArray();
                E oldValue = get(elements, index);
    
                if (oldValue != element) {
                    int len = elements.length;
                    //数组拷贝
                    Object[] newElements = Arrays.copyOf(elements, len);
                    newElements[index] = element;
                    setArray(newElements);
                } else {
                    // Not quite a no-op; ensures volatile write semantics
                    setArray(elements);
                }
                return oldValue;
            } finally {
                lock.unlock();
            }
        }    
    

      


      

  • 相关阅读:
    JMeter--聚合报告之 90% Line 正确理解
    jmeter--函数助手对话框之参数详解
    测试理论--如何根据需求设计测试用例
    java jdk 1.6 下载
    linux磁盘满时,如何定位并删除文件
    linux mysql 新增用户 分配权限
    Hibernate 中多对多(many-to-many)关系的查询语句
    springMVC的url-pattern /和/*的区别
    thinkphp多表关联并且分页
    thinkphp 模板里嵌入 php代码
  • 原文地址:https://www.cnblogs.com/cnxieyang/p/12766231.html
Copyright © 2011-2022 走看看