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();
        }
    }    

  


  

原文地址:https://www.cnblogs.com/cnxieyang/p/12766231.html