zoukankan      html  css  js  c++  java
  • 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在添加元素和修改元素的操作上都进行了加锁,所以在两个操作上是会消耗一定的性能的,因此这个类主要使用在读多写少的场景上。

  • 相关阅读:
    Eclipse常见配置及常用插件
    杂记
    表单双引号问题
    兼容ie的jquery ajax文件上传
    Markdown 學習
    jstl c标签
    java 在接口里函数不能重载?
    【转】Eclipse里项目名有红叉,但是展开后里面又没有红叉叉
    Android性能优化之一:ViewStub
    Merge用法
  • 原文地址:https://www.cnblogs.com/baichendongyang/p/13235465.html
Copyright © 2011-2022 走看看