欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

(二) 并发容器之 CopyOnWriteArraySet

程序员文章站 2024-01-07 21:45:34
同样, 用 List 的并发容器,当然也存在 Set 的并发容器,J.U.C 包下同样存在 CopyOnWriteArraySetCopyOnWriteArraySetpublic class CopyOnWriteArraySet extends AbstractSet implements java.io.Serializable { // 该类的所有操作由 CopyOnWriteArrayList 完成 private final C...

同样, 用 List 的并发容器,当然也存在 Set 的并发容器,J.U.C 包下同样存在 CopyOnWriteArraySet

CopyOnWriteArraySet

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

    // 该类的所有操作由 CopyOnWriteArrayList 完成
    private final CopyOnWriteArrayList<E> al;

    // 在实例化 CopyOnWriteArraySet 的时候,其实就是实例化了 CopyOnWriteArrayList
    public CopyOnWriteArraySet() {
        al = new CopyOnWriteArrayList<E>();
    }
    ....
}

当然,这个是一个 Set ,在添加元素的时候需要多判断一下,直接查看他得 add(E) 方法

//发现它是调用了 CopyOnWriteArrayList 的 addIfAbsent(E) 方法
public boolean addIfAbsent(E e) {
    Object[] snapshot = getArray();
    return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
    addIfAbsent(e, snapshot);
}
-----------------
// 查看 indexOf,从方法名就可以看出,这是一个得到元素下标的方法
// 至于是哪个元素的下标,根据 Set 的性质,你也猜的出来了
private static int indexOf(Object o, Object[] elements,
                           int index, int fence) {
    if (o == null) {
        for (int i = index; i < fence; i++)
            if (elements[i] == null)
                return i;
    } else {
        for (int i = index; i < fence; i++)
            if (o.equals(elements[i]))
                return i;
    }
    // 就是遍历数组,如果存在相同元素,返回它的下标,如果不存在,返回 -1
    return -1;
}
-------------------------------
// 这里还需要注意,在高并发的情况下, 如果当前线程已经检查完数组,不存在相同元素
// 在返回 -1 后, 有可能另一个线程将数组的元素修改了,此时是否存在相同元素? 不得而知
// 所以在 addIfAbsent(E, Object[]) 方法需要判断一次
// 接着调用 addIfAbsent(E, Object[]) 方法
private boolean addIfAbsent(E e, Object[] snapshot) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 最新的数组,用于与快照比较
        Object[] current = getArray();
        int len = current.length;
        // snapshot 相当于快照,为已经检查完毕的数组(不是最新的)
        if (snapshot != current) {
            // 如果快照与最新的数组不匹配
            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;
            // 再次检查, 需要添加的元素与最新的数组是否存在相同元素
            if (indexOf(e, current, common, len) >= 0)
                return false;
        }
        // 如果不存在相同元素, 复制一份数组,追加元素
        Object[] newElements = Arrays.copyOf(current, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}


说到 Set, 再来看看 HashSet

HashSet

public class HashSet<E> extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {
    
    // HashSet 的所有操作都是交给 HashMap 完成
    private transient HashMap<E,Object> map;

    // 由于 HashMap 需要 K-V 两个参数,该常量作为 value 放入 HashMap 
    private static final Object PRESENT = new Object();

    // 同 CopyOnWriteArraySet, 在实例化 HashSet 的时候其实就是实例化了 HashMap
    public HashSet() {
        map = new HashMap<>();
    }
    ....
}

由于 HashMap 的特性 : Key 不唯一,JDK 直接使用 HashMap 来操作 HashSet, 查看 HashSet 的 add 方法

public boolean add(E e) {
    return map.put(e, PRESENT)==null;       
}

就是调用了 HashMap 的 put 方法, 使用一个常量作为 value 放入 HashMap 中

本文地址:https://blog.csdn.net/Gp_2512212842/article/details/107691003

相关标签: 并发

上一篇:

下一篇: