天天看點

【從入門到放棄-Java】并發程式設計-JUC-CopyOnWriteArraySet

前言

CopyOnWriteArraySet也是JUC下常用容器,其底層實作是基于CopyOnWriteArrayList的,關于CopyOnWriteArrayList的詳情可以檢視

【從入門到放棄-Java】并發程式設計-JUC-CopyOnWriteArrayList

,接下來我們看下源碼。

CopyOnWriteArraySet

/**
 * Creates an empty set.
 */
 //簡單粗暴,隻有一個成員變量al,是CopyOnWriteArrayList類型的,初始化時,new一個CopyOnWriteArrayList指派給al。
public CopyOnWriteArraySet() {
    al = new CopyOnWriteArrayList<E>();
}

/**
 * Creates a set containing all of the elements of the specified
 * collection.
 *
 * @param c the collection of elements to initially contain
 * @throws NullPointerException if the specified collection is null
 */
public CopyOnWriteArraySet(Collection<? extends E> c) {
    if (c.getClass() == CopyOnWriteArraySet.class) {
        @SuppressWarnings("unchecked") CopyOnWriteArraySet<E> cc =
            (CopyOnWriteArraySet<E>)c;
        al = new CopyOnWriteArrayList<E>(cc.al);
    }
    else {
        al = new CopyOnWriteArrayList<E>();
        al.addAllAbsent(c);
    }
}           

add

/**
 * Adds the specified element to this set if it is not already present.
 * More formally, adds the specified element {@code e} to this set if
 * the set contains no element {@code e2} such that
 * {@code Objects.equals(e, e2)}.
 * If this set already contains the element, the call leaves the set
 * unchanged and returns {@code false}.
 *
 * @param e element to be added to this set
 * @return {@code true} if this set did not already contain the specified
 *         element
 */
public boolean add(E e) {
    //調用CopyOnWriteArrayList的addIfAbsent方法。
    return al.addIfAbsent(e);
}

//看下CopyOnWriteArrayList的addIfAbsent方法如何實作。
/**
 * Appends the element, if not present.
 *
 * @param e element to be added to this list, if absent
 * @return {@code true} if the element was added
 */
public boolean addIfAbsent(E e) {
    Object[] snapshot = getArray();
    //先查找e是否在存在,不存在的話 調用addIfAbsent(E e, Object[] snapshot)方法
    return indexOfRange(e, snapshot, 0, snapshot.length) < 0
        && addIfAbsent(e, snapshot);
}

/**
 * A version of addIfAbsent using the strong hint that given
 * recent snapshot does not contain e.
 */
private boolean addIfAbsent(E e, Object[] snapshot) {
    //加鎖
    synchronized (lock) {
        Object[] current = getArray();
        int len = current.length;
        //加鎖後check下快照是否被改動
        if (snapshot != current) {
            //如果改動過,則判斷改動後的數組是否包含要添加的元素,如果有的話,則傳回失敗
            // Optimize for lost race to another addXXX operation
            int common = Math.min(snapshot.length, len);
            for (int i = 0; i < common; i++)
                if (current[i] != snapshot[i]
                    && Objects.equals(e, current[i]))
                    return false;
            if (indexOfRange(e, current, common, len) >= 0)
                    return false;
        }
        //驗證數組中沒有此元素,則在末尾添加。
        Object[] newElements = Arrays.copyOf(current, len + 1);
        newElements[len] = e;
        //替換原數組。
        setArray(newElements);
        return true;
    }
}           

addAll

/**
 * Adds all of the elements in the specified collection to this set if
 * they're not already present.  If the specified collection is also a
 * set, the {@code addAll} operation effectively modifies this set so
 * that its value is the <i>union</i> of the two sets.  The behavior of
 * this operation is undefined if the specified collection is modified
 * while the operation is in progress.
 *
 * @param  c collection containing elements to be added to this set
 * @return {@code true} if this set changed as a result of the call
 * @throws NullPointerException if the specified collection is null
 * @see #add(Object)
 */
public boolean addAll(Collection<? extends E> c) {
    //直接調用CopyOnWriteArrayList的addAllAbsent方法。
    return al.addAllAbsent(c) > 0;
}

/**
 * Appends all of the elements in the specified collection that
 * are not already contained in this list, to the end of
 * this list, in the order that they are returned by the
 * specified collection's iterator.
 *
 * @param c collection containing elements to be added to this list
 * @return the number of elements added
 * @throws NullPointerException if the specified collection is null
 * @see #addIfAbsent(Object)
 */
public int addAllAbsent(Collection<? extends E> c) {
    //将要添加的元素變為數組
    Object[] cs = c.toArray();
    if (cs.length == 0)
        return 0;
    //加鎖
    synchronized (lock) {
        Object[] es = getArray();
        int len = es.length;
        int added = 0;
        // uniquify and compact elements in cs
        //周遊要添加的數組,如果在List的數組中不存在此元素,則添加到cs數組中
        for (int i = 0; i < cs.length; ++i) {
            Object e = cs[i];
            if (indexOfRange(e, es, 0, len) < 0 &&
                indexOfRange(e, cs, 0, added) < 0)
                cs[added++] = e;
        }
        if (added > 0) {
            Object[] newElements = Arrays.copyOf(es, len + added);
            //将cs數組的前added個元素添加在List數組後面
            System.arraycopy(cs, 0, newElements, len, added);
            //替換原數組
            setArray(newElements);
        }
        return added;
    }
}           

remove

/**
 * Removes the specified element from this set if it is present.
 * More formally, removes an element {@code e} such that
 * {@code Objects.equals(o, e)}, if this set contains such an element.
 * Returns {@code true} if this set contained the element (or
 * equivalently, if this set changed as a result of the call).
 * (This set will not contain the element once the call returns.)
 *
 * @param o object to be removed from this set, if present
 * @return {@code true} if this set contained the specified element
 */
public boolean remove(Object o) {
    //直接調用CopyOnWriteArrayList的remove
    return al.remove(o);
}           

總結

通過源碼分析,我們了解到,CopyOnWriteArraySet主要是依賴CopyOnWriteArrayList來實作各方法的。

是以與CopyOnWriteArrayList一樣,更适用于讀多寫少的并發操作中。

詳細原因在

中以及解釋過了,這裡不再贅述。

值得一提的是,常用的非并發容器HashSet,是基于HashMap實作的,利用HashMap中Entry的key做到唯一值,Entry的value是一個不可變靜态對象Object。但是JUC中并發Set是卻不是基于Map的,學習完這三章并發容器,你能回答這是為什麼嗎?可以在評論中留言,我們一起探讨下哦~

更多文章

見我的部落格:

https://nc2era.com

written by

AloofJr

,轉載請注明出處