juc 下的集合之六 (CopyOnWriteArrayList )

一、基本思想

       ArrayList的一个变体,通过对原来数组的拷贝,来保证不同操作情况下的线程安全。

      这个容器类内部有大量的数组拷贝操作

二、源码解析

     2.1 基本数据结构

    /** The lock protecting all mutators */
    transient final ReentrantLock lock = new ReentrantLock();

    /** The array, accessed only via getArray/setArray. */
    private volatile transient Object[] array;

   很简单,里面一个数组而已,声明成volatile保证一个线程的修改对其它线程立即可见

 

    2.2 get操作

public E get(int index) {
        return (E)(getArray()[index]); //数组的简单遍历操作,没有锁,没有拷贝
    }

 final Object[] getArray() {
        return array;
    }

public Object[] toArray()//返回的是一个数组副本
        Object[] elements = getArray();
	return Arrays.copyOf(elements, elements.length);
    }

 

 

    2.3 add操作

public void add(int index, E element) {
	final ReentrantLock lock = this.lock;
	lock.lock(); //可重入锁保证线程安全
	try {
	    Object[] elements = getArray(); //获取数组对象
	    int len = elements.length;
	    if (index > len || index < 0)
		throw new IndexOutOfBoundsException("Index: "+index+
						    ", Size: "+len);
	    Object[] newElements;
	    int numMoved = len - index;
	    if (numMoved == 0)
		newElements = Arrays.copyOf(elements, len + 1); //不需要移动,直接拷贝一个新的数组
	    else {
		newElements = new Object[len + 1];
		System.arraycopy(elements, 0, newElements, 0, index); //拷贝待移动之前的数组
		System.arraycopy(elements, index, newElements, index + 1,
				 numMoved);//拷贝待移动的数组
	    }
	    newElements[index] = element;//设置值
	    setArray(newElements);//将新的数组作为修改后的数组
	} finally {
	    lock.unlock();//释放锁放在final中
	}
    }

 2.4 remove 操作

 public E remove(int index) {
	final ReentrantLock lock = this.lock;
	lock.lock();
	try {
	    Object[] elements = getArray();
	    int len = elements.length;
	    Object oldValue = elements[index];
	    int numMoved = len - index - 1;
	    if (numMoved == 0)
		setArray(Arrays.copyOf(elements, len - 1));
	    else {
		Object[] newElements = new Object[len - 1];
		System.arraycopy(elements, 0, newElements, 0, index);
		System.arraycopy(elements, index + 1, newElements, index,
				 numMoved);
		setArray(newElements);
	    }
	    return (E)oldValue;
	} finally {
	    lock.unlock();
	}
    }

 很简单,和add操作基本一样

 

三、适用范围

       官方解释,来自JDK源码中的注释。

       尽管数组的大量拷贝的代价很大,但是在某些情况下可能会更加有效。比如说你需要用多个线程对数组大量的遍历,而你又不想同步你的遍历的时候。

因为你每次遍历其实是你当前数组的一个快照,所以你的程序永远不会出现"ConcurrentModifacationException"。

       内存一致性问题:一个线程在处理这个数组的时候都是处理这个数组的副本,因此线程在处理过程中的这段时间,无法获得其它的线程对此数组的更改。

 

四、测试

 

//TODO :)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值