寫時複制的容器。通俗的了解是當我們往一個容器添加元素的時候,不直接往目前容器添加,而是先将目前容器進行Copy,複制出一個新的容器,然後新的容器裡添加元素,添加完元素之後,再将原容器的引用指向新的容器。
這樣做的好處是我們可以對容器進行并發的讀,而不需要加鎖,因為目前容器不會添加任何元素。是以寫時複制容器也是一種讀寫分離的思想,讀和寫不同的容器。如果讀的時候有多個線程正在向容器添加資料,讀還是會讀到舊的資料,因為寫的時候不會鎖住舊的,隻能保證最終一緻性。
适用讀多寫少的并發場景,常見應用:白名單/黑名單, 商品類目的通路和更新場景。
CopyOnWriteArrayList和CopyOnWriteArraySet就是寫時複制容器。
CopyOnWriteArrayList 類圖
底層依然是數組,基于ReentrantLock來做并發控制。
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();
}
}
CopyOnWriteArraySet類圖
CopyOnWriteArraySet就是基于CopyOnWriteArrayList來實作的。
測試方法
@Test
public void testCopyOnWrite() {
CopyOnWriteArrayList<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
copyOnWriteArrayList.add(5);
copyOnWriteArrayList.add(3);
copyOnWriteArrayList.add(4);
copyOnWriteArrayList.add(2);
copyOnWriteArrayList.add(3);
System.out.println(copyOnWriteArrayList);
CopyOnWriteArraySet<Integer> copyOnWriteArraySet = new CopyOnWriteArraySet<>();
copyOnWriteArraySet.add(5);
copyOnWriteArraySet.add(3);
copyOnWriteArraySet.add(4);
copyOnWriteArraySet.add(2);
copyOnWriteArraySet.add(3);
System.out.println(copyOnWriteArraySet);
}
輸出:
[5, 3, 4, 2, 3]
[5, 3, 4, 2]
源碼
https://github.com/wyh-spring-ecosystem-student/spring-boot-student/tree/releases