1.CopyOnWrite容器即写时复制的容器。当我们新添加一个元素到容器时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。下面是CopyOnWriteArrayList源码实现:
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final long serialVersionUID = 8673264195747942595L;
/** 定义一个重入锁,用来保护元素非原子操作时线程安全 */
final transient ReentrantLock lock = new ReentrantLock();
/** 使用数组来存放数据 */
private transient volatile Object[] array;
//初始化数组容量
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
final void setArray(Object[] a) {
array = a;
}
//添加一个元素,每次宽容为当前容量+1
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();
}
}
//获取存放数据的实例对象
final Object[] getArray() {
return array;
}
@SuppressWarnings("unchecked")
/**
* 开始拷贝
* @param original 原数组变量
* @param newLength 新数组容量长度
* @return
*/
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
//获取指定下标的元素
public E get(int index) {
return get(getArray(), index);
}
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
return (E) a[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 - 1;
if (numMoved == 0) // 判断是否是移除最后一个元素
setArray(Arrays.copyOf(elements, len - 1)); // 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();
}
}
// 指定下标设置一个元素,如果值相同不做任何操作
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();
}
}
........
}