(十)并发集合——CopyOnWrite集合

CopyOnWrite

这种集合的意思是修改的时候复制一个新的数组,在新的数组修改完成再替换原来的数组;读的时候不加锁,写的时候才加锁,读读不互斥,读写不互斥,写写才互斥。

CopyOnWriteArrayList

相当于线程安全的ArrayList,核心源码如下,有关读的方法,例如contains、indexOf都没有加锁,有关写的方法,例如add、remove都加了锁,其他写方法也跟add,remove类似。

//写锁
final transient ReentrantLock lock = new ReentrantLock();

//内部数组,只能通过getArray/setArray方法访问
//设置getArray/setArray是为了给CopyOnWriteArraySet使用
// 用volatile修饰是为了替换时对其他线程可见
private transient volatile Object[] array;

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

final void setArray(Object[] a) {
	array = a;
}
public boolean contains(Object o) {
	Object[] elements = getArray();
	return indexOf(o, elements, 0, elements.length) >= 0;
}

public int indexOf(Object o) {
	Object[] elements = getArray();
	return indexOf(o, elements, 0, elements.length);
}

/**
 * @param o 要查找的元素
 * @param elements 内部数组
 * @param index 起始位置
 * @param fence 结束位置
 * @return 存在返回下标,不存在返回-1
 */
private static int indexOf(Object o, Object[] elements,
                           int index, int fence) {
	if (o == null) {
		for (int i = index; i < fence; i++)
			if (elements[i] == null)
				return i;
	} else {
		for (int i = index; i < fence; i++)
			if (o.equals(elements[i]))
				return i;
	}
	return -1;
}

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)
			//如果是增加到末尾位置,直接复制一个比原数组长度大1的数组,然后将新元素赋值到末尾位置
			newElements = Arrays.copyOf(elements, len + 1);
		else {
			//如果不是增加到末尾位置,先复制0-(index-1)位置的元素,然后再将老数组index之后的元素复制到新数组index+1及之后的位置
			newElements = new Object[len + 1];
			System.arraycopy(elements, 0, newElements, 0, index);
			System.arraycopy(elements, index, newElements, index + 1,
					numMoved);
		}
		//给index位置赋值
		newElements[index] = element;
		//用新数组替换原来的老数组
		setArray(newElements);
	} finally {
		lock.unlock();
	}
}

public E remove(int index) {
	final ReentrantLock lock = this.lock;
	lock.lock();
	try {
		//获取数组
		Object[] elements = getArray();
		int len = elements.length;
		//获取index索引的元素
		E oldValue = get(elements, index);
		//需要移动的元素个数
		int numMoved = len - index - 1;
		if (numMoved == 0)
			//如果移除的元素在末尾,直接复制一个比原数组长度小1的数组
			setArray(Arrays.copyOf(elements, len - 1));
		else {
			//如果移除的元素不在末尾位置,先复制0-(index-1)位置的元素,然后再将老数组index+1之后的元素复制到新数组index及之后的位置
			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();
	}
}

CopyOnWriteArraySet

不允许重复元素出现,依赖了CopyOnWriteArrayList,实现委托给了CopyOnWriteArrayList的方法。

private final CopyOnWriteArrayList<E> al;

猜你喜欢

转载自blog.csdn.net/qq_32076957/article/details/128333727