天天看點

JAVA并發容器-寫時複制容器

寫時複制的容器。通俗的了解是當我們往一個容器添加元素的時候,不直接往目前容器添加,而是先将目前容器進行Copy,複制出一個新的容器,然後新的容器裡添加元素,添加完元素之後,再将原容器的引用指向新的容器。

這樣做的好處是我們可以對容器進行并發的讀,而不需要加鎖,因為目前容器不會添加任何元素。是以寫時複制容器也是一種讀寫分離的思想,讀和寫不同的容器。如果讀的時候有多個線程正在向容器添加資料,讀還是會讀到舊的資料,因為寫的時候不會鎖住舊的,隻能保證最終一緻性。

适用讀多寫少的并發場景,常見應用:白名單/黑名單, 商品類目的通路和更新場景。

CopyOnWriteArrayList和CopyOnWriteArraySet就是寫時複制容器。

CopyOnWriteArrayList 類圖

JAVA并發容器-寫時複制容器

底層依然是數組,基于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類圖

JAVA并發容器-寫時複制容器

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​​

layering-cache