【笔记】Java并发编程之美-Java并发包中并发List源码剖析

介绍

并发包中的并发List只有CopyOnWriteArrayList。CopyOnWriteArrayList是一个线程安全的ArrayList,对其进行的修改操作都是在底层的一个复制的数组(快照)上。

每个CopyOnWriteArrayList对象里面有一个array数组对象用来存放具体元素,ReentrantLock独占锁对象用来保证同时只有一个线程对array进行修改。ReentrantLock是独占锁。

主要方法源码解析
初始化

无参CopyOnWriteArrayList(),入参为toCopyIn:CopyOnWriteArrayList(E[] toCopyIn),入参为集合:CopyOnWriteArrayList(Collections<? extends E> c)。

添加元素

有add(E e)、add(int index, E element)、addifAbsent(E e)和addAllAbsent(Collection<? extends E> c)等。原理类似,以下讲解add(E e)。

public boolean add(E e) { 
	//获取独占锁(1) 
	final ReentrantLock lock = this.lock; 
	lock.lock() ; 
	try {
		//(2)获取array 
		Object[] elements= getArray() ; 

		//(3)复制array到新数组,添加元素到新数组
		int len = elements.length; 
		Object[] newElements = Arrays.copyOf(elements, len + 1) ;
		newElements[len] = e ;
		 
		//(4)使用新数组替换添加前的数组 
		setArray(newElements) ; 
		return true; 
	} finally {
		//(5)释放独占锁
		lock.unlock();
	}
}
获取指定位置元素
public E get(int index) { 
	return get (getArray() , index );
}
final Object[] getArray() { 
	return array;
} 
private E get(Object[] a , int index) { 
	return (E) a[index];
}

分为两步,先获取数组array,再通过下标index访问元素,此过程没有加锁同步。若另一个线程在本线程获取数组array之后和访问元素之前改变了数组,则出现了写时复制策略产生的弱一致性问题。

修改指定元素
public E set(int index, E element) { 
	//获取独占锁
	final ReentrantLock lock = this.lock; 
	lock.lock() ; 
	try {
		//获取array 和 旧值
		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 { 
			//若相等,为了保证volatile语义,仍需重新设置array
			setArray(elements) ; 
		}
		return oldValue; } 
	}finally {
		//释放独占锁
		lock.unlock();
	}
}
删除元素

使用 E remove(int index)、boolean remove(Object o)和 boolean remove(Object o, Object[] snapshot, int index)等方法,它们的原理一样。下面讲解remove(int index)方法。

public E remove(int index) { 
	//获取独占锁
	final ReentrantLock lock= this.lock; 
	lock.lock(); 
	try { 
		//获取数组 
		Object[] elements= getArray(); 
		int len = elements.length; 
		
		//获取指定元素
		E oldValue = get(elements, index); 
		int numMoved = len - index - l ; 
		
		//若删除的是最后一个元素 
		if(numMoved == 0) 
			setArray(Arrays.copyOf(elements, len - l)) ; 
		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 oldValue;
    } finally {
        lock.unlock();
    }
}
弱一致性的迭代器

所谓弱一致性是指返回迭代器后,其他线程对 list 的增删改对迭代器是不可见的。

总结

CopyOnWriteArrayList 使用写时复制的策略来保证 list 的一致性,而获取一修改一写入三步操作并不是原子性的,所以在增删改的过程中都使用了独占锁,来保证在某个时间只有一个线程能对 list 数组进行修改。另外 CopyOnWriteArrayList 提供了弱一致性的迭代器,从而保证在获取迭代器后,其他线程对 list 的修改是不可见的,迭代器遍历的数组是一个快照。 另外,CopyOnWriteArraySet 的底层就是使用它实现的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值