CopyOnWriteArrayList和CopyOnWriteArraySet也是线程安全的集合,其中所有的修改线程对底层数组进行复制。当线程对其读,直接读取集合本身无需加锁和阻塞;当线程对其写入(包括调用add,remove,set等方法),该集合会在底层复制一份数组,接下来对数组进行写入操作。由于对其写入都是对数组副本的操作,因此是线程安全的。由于每次写入都要复制数组会导致性能很差,因此适合读操作远大于写入的场景,例如缓存。
具体源码:
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock(); //加锁
try {
Object[] elements = getArray(); //初始化一个elements变量引用原始数组
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1); //利用java.util.Arrays工具方法拷贝elements
newElements[len] = e;
setArray(newElements); //将原始数组替换掉
return true;
} finally {
lock.unlock(); //释放锁
}
}
里面的具体函数实现:
private transient volatile Object[] array;
final Object[] getArray() {
return array;}
final void setArray(Object[] a) {
array = a;
}
其他的remove、set方法同理,都是先加锁,并且加的是ReentrantLock锁,Lock和ReadWriteLock是java5的两个根接口,并为Lock接口提供了ReentrantLock实现类,为ReadWriteLock接口提供了ReentrantReadWriteLock实现类,它提供了三种锁模式:Writing、ReadingOptimistic、Reading。重入性的意思是一个线程可以对已被加锁的ReentrantLock锁再次加锁,ReentrantLock对象会维持一个计数器来追踪lock()的嵌套调用。