码迷,mamicode.com
首页 > 其他好文 > 详细

CopyOnWriteArrayList 源码阅读与分析

时间:2015-05-04 13:56:57      阅读:156      评论:0      收藏:0      [点我收藏+]

标签:concurrent   java   源码   线程安全   并发   

CopyOnWriteArrayList 源码阅读与分析

      CopyOnWriteArrayList是并发包下的一个线程安全、可以实现高并发的ArrayList

首先来看看它的构造方法:

final void setArray(Object[] a) {

       array = a;

    }

 

   /**

    * Creates an empty list.

    */

   public CopyOnWriteArrayList() {

       setArray(new Object[0]);

}

    可以看到,它和ArrayList有一定的不同,它是创建一个大小为 0 的数组。ArrayList是一个空数组。

 

     下面来看看它的add(E) 方法,进一步了解它的实现原理

      顾名思义,它的名字叫CopyOnWriteArrayList(),也就是在进行写操作时,如add,remove时,会进行复制操作,即创建一个新的比当前数长度大1的新的数组,再把旧的值复制过去。代码如下所示:

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 方法并没有使用 synchronized 关键字来实现互斥,而是通过使用ReentrantLock 来进行加锁操作。可以看到,它的实现比较简单,首先加锁,然后创建一个比目前数组长度大1的新数组,再把旧数组中的值复制到新的数组中去。然后再调用setArray方法改变引用,完成add的操作,结束以后,再释放锁。

       之所以再每次的修改数组操作(add/remove等)之后,会新建一个数组,修改完毕之后,再将原来的引用指向新的数组。是为了保证数组被一个线程遍历时,没有其他线程对数组进行修改。所以也就不会抛出ArrayList并发情况下出现的ConcurrentModificationException错误。

 

 

     下面再来看看get(int) 方法

   先来看看代码的实现:

 public E get(int index) {

       return get(getArray(), index);

    }


@SuppressWarnings("unchecked")

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

       return (E) a[index];

    }

 

 

       可以看到,非常简单,直接获取当前数组对应的位置的元素,但是由于方法没有进行任何的加锁操作,所以可能会出现读到脏数据的现象,这样可以有比较高的性能,所以在修改少,读取多的场景下。它开始很好的选择。

 

 

 

  再来看看remove(E) 方法

   和 add 方法一样,此方法也通过ReetrantLock 来保证其线程安全。

     首先判断要删除的元素是不是最后一个元素:

          Object[] elements = getArray();

           int len = elements.length;

           E oldValue = get(elements, index);

           int numMoved = len - index - 1;

       如果是最后一个元素,就把原来的数组除去最后一个元素都 复制到新的数组中,再改变引用。如下所示:

 if(numMoved == 0)

 setArray(Arrays.copyOf(elements,len - 1));

 

         如果不是最后一个元素,就进行两次复制,先把原数组从0到index-1的数据复制到新的数组,再把index+1到最后一个元素复制到新的数组中,最后再改变引用。完成这些以后,就完成了删除的操作。

        可以看到CopyOnWriteArrayList没有像ArrayList一样的动态扩容机制,而是再每次对数组修改时都会新建一个新的数组,为了避免ConcurrentModificationException异常,但是这样也会对性能有一定的影响。而且它并没有对读操作进行任何的线程安全的操作,所以可能会出现读到脏数据的情况。所以当写操作比较多时,使用CopyOnWriteArrayList个人感觉不是好的选择。

 

CopyOnWriteArrayList 源码阅读与分析

标签:concurrent   java   源码   线程安全   并发   

原文地址:http://blog.csdn.net/qfycc92/article/details/45479059

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!