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()的嵌套調用。