Java 并发编程之 CopyOnWriteArraySet

Java juc 大约 3079 字

介绍

线程安全的,适合并发的集合类,对比HashSet

原理

底层实际就是CopyOnWriteArrayList

具体可参考前一篇文章:Java 并发编程之 CopyOnWriteArrayList

示例代码

public class CopyOnWriteArraySetDemo {

    private static CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                set.add(String.valueOf(ThreadLocalRandom.current().nextInt(100000)));
            }).start();
        }

        TimeUnit.SECONDS.sleep(1);

        System.out.println(set.toString());
    }

}

源码分析

CopyOnWriteArraySet底层就是CopyOnWriteArrayList,通过CopyOnWriteArrayListaddIfAbsent()方法保证是一个Set集合。

public class CopyOnWriteArraySet<E> extends AbstractSet<E> implements java.io.Serializable {

    private final CopyOnWriteArrayList<E> al;

    // 构造函数,初始化一个 CopyOnWriteArrayList
    public CopyOnWriteArraySet() {
        al = new CopyOnWriteArrayList<E>();
    }

    public boolean add(E e) {
        return al.addIfAbsent(e);
    }

}

public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {

    // 如果不存在,则添加
    public boolean addIfAbsent(E e) {
        Object[] snapshot = getArray();
        // indexOf 寻找元素索引,如果找到了,说明存在改元素,则不添加。
        // 否则调用 addIfAbsent(E e, Object[] snapshot) 方法
        return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
            addIfAbsent(e, snapshot);
    }

    final transient ReentrantLock lock = new ReentrantLock();

    private transient volatile Object[] array;

    final Object[] getArray() {
        return array;
    }

    private static boolean eq(Object o1, Object o2) {
        return (o1 == null) ? o2 == null : o1.equals(o2);
    }

    // 入参为添加的元素和 CopyOnWriteArrayList 底层数组的快照
    private boolean addIfAbsent(E e, Object[] snapshot) {
        final ReentrantLock lock = this.lock;
        // 加悲观锁
        lock.lock();
        try {
            // 再次获取当前 CopyOnWriteArrayList 底层数组的快照
            Object[] current = getArray();
            int len = current.length;
            // 如果入参的数组快照不等于再次获取的数组快照
            if (snapshot != current) {
                // 考虑到被其他 addXXX 操作修改过
                int common = Math.min(snapshot.length, len);
                for (int i = 0; i < common; i++)
                    // 如果再次获取的数组的相同索引处的元素不等于入参的相同索引处的元素,
                    // 并且要添加的元素等于再次获取的数组的中的元素,则表示已经存在,直接返回
                    if (current[i] != snapshot[i] && eq(e, current[i]))
                        return false;
                // 如果当前元素的所有 >=0,说明已经存在,直接返回
                if (indexOf(e, current, common, len) >= 0)
                        return false;
            }
            // 往根据底层数组再新建一个和底层数组包含同样数据的数组,并且长度增加 1
            Object[] newElements = Arrays.copyOf(current, len + 1);
            // 添加的元素加入到数组中
            newElements[len] = e;
            // 把底层数组替换为新数组
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

}
阅读 80 · 发布于 2021-11-02

————        END        ————

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批