CopyOnWriteArrayList源码分析

前言:

当我们想要用ArrayList,又想要保证线程安全的时候,可以考虑使用CopyOnWriteArrayList这个类。因为如果使用Vector的话,虽然可以保证线程安全,但是因为在Vector里面是用synchronized修饰的,所以开销会比较大。因此考虑使用CopyOnWriteArrayList。

一.概述

CopyOnWriteArrayList是concurrent 并发包下的一个类,由名称可以看出他是基于数据的一个链表,这个链表在进行写操作的时候回进行copy,接下来就通过源码来看看这个类。

二.源码分析

1.属性:

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;                           

    final Object[] getArray() {      
        return array;                
    }                                
                                 
    /**                              
     * Sets the array.               
     */                              
    final void setArray(Object[] a) {
        array = a;                   
    }                                
    public CopyOnWriteArrayList() { 
        setArray(new Object[0]);    
    }                               

首先先看CopyOnWriteArrayList这个类的相关属性,可以看到他实现了RandomAccess接口,所以支持随机访问的。然后定义了一个不被序列化的ReentranLock实例对象,通过这个对象实现了加锁同步的操作,关于这个ReentranLock这个类可以参照这个:https://blog.csdn.net/striveb/article/details/83421107,此外,还有一个不被序列化并且具有可见性的数组,在构造方法里面生成了这样的一个空数组。

2.add方法

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

由上面源码可以看出,添加元素,即写操作是通过ReentranLock加锁实现的。由上面属性可知,写操作是先复制一个数组,然后在这个数组上进行新增元素,写操作结束之后将原始数组指向新的复制数组。

3.get方法

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

由上面源码可以看出,读操作就是直接返回数组对应的值,并没有加锁。

4.set方法

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 semantic
            setArray(elements);                                  
        }                                                        
        return oldValue;                                         
    } finally {                                                  
        lock.unlock();                                           
    }                                                            
}                                                                

由上面源码可知,set方法也是通过加锁实现修改元素的,其中也是先复制了数组,跟add方法基本相似。

三、使用场景

由上面的分析可知,CopyOnWriteArrayList在添加元素和修改元素的操作上都进行了加锁,所以在两个操作上是会消耗一定的性能的,因此这个类主要使用在读多写少的场景上。

原文地址:https://www.cnblogs.com/baichendongyang/p/13235465.html