下面我们来说一下CopyOnWriteArrayList。这个类是一个线程安全的集合,通过copy-on-write机制实现的,下面我们就来看一下CopyOnWriteArrayList是怎么实现的。
先来看一下CopyOnWriteArrayList的属性
/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
有一个ReentrantLock,还有一个Object数组
下面我们来看一下get方法
public E get(int index) {
return get(getArray(), index);
}
再到getArray方法
final Object[] getArray() {
return array;
}
就是从数组对应的位置取出元素返回
看到这里,你可能会觉得好像也没有什么特别的啊,是怎么实现copy-on-write机制的呢?别急,我们下面来看set方法
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
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 {
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
}
return oldValue;
} finally {
lock.unlock();
}
}
先加锁,然后从数组里取出原来的元素,然后判断原来的元素不等于要set的元素的话,就用Arrays的copyOf方法复制一份新的数组,把要set的元素set进去,并把新的数组替换掉原来的数组,否则直接把原来的数组再放回去,确保这个过程的内存可见性,然后解锁。
再来看一下add方法
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();
}
}
先加锁,然后取到当前的数组,调用Arrays的copyOf方法复制一份新的数组,把元素添加进去,然后解锁。
再来看一下在特定位置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();
}
}
这个要复杂一点,先加锁,然后获取到当前的数组,判断数组的位置,然后用当前数组的长度-索引值,得到移动的元素的数量,如果没有元素被移动,则调用Arrays的copyOf方法复制一份新的数组,否则创建一个比原来数组容量大1的新的数组,然后调用System.arraycopy方法复制一份新的数组,然后将元素添加到对应的位置上,然后将新的数组替换掉原来的数组,然后解锁。
再来看一下remove方法
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 - 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 oldValue;
} finally {
lock.unlock();
}
}
先加锁,然后获取到原来的元素,然后用数组的容量-索引值-1算出被移动的元素的数量,然后判断如果没有元素被移动,则调用Arrays的copyOf方法复制一份新的数组,并替换掉原来的数组,否则创建一个新的数组,容量是原来的容量-1,然后调用System.arraycopy方法复制一份新的数组,然后将要删除的元素的后一个元素往前移动,完成元素的删除,然后将新的数组替换掉原来的数组,然后解锁。
CopyOnWriteArrayList的分析就到这里了。