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;