CopyOnWriteArraySet of Java Concurrent Container

In this article, let's talk about CopyOnWriteArraySet in Java concurrent containers.

CopyOnWriteArraySet implements related functions based on the CopyOnWriteArrayList container, as follows:

    private final CopyOnWriteArrayList<E> al;

    /**
     * Creates an empty set.
     */
    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) {
        al = new CopyOnWriteArrayList<E>();
        al.addAllAbsent(c);
    }

All the various operation methods on elements provided in this container are actually operations on the CopyOnWriteArrayList container. The container of CopyOnWriteArrayList is briefly explained in another article "CopyOnWriteArrayList of Java Concurrent Containers ".

Let's see how this container adds, deletes and views elements.

/**
     * Adds the specified element to this set if it is not already present.
     * More formally, adds the specified element <tt>e</tt> to this set if
     * the set contains no element <tt>e2</tt> such that
     * <tt>(e==null ? e2==null : e.equals(e2))</tt>.
     * If this set already contains the element, the call leaves the set
     * unchanged and returns <tt>false</tt>.
     *
     * @param e element to be added to this set
     * @return <tt>true</tt> if this set did not already contain the specified
     *         element
     */
    public boolean add(E e) {
        return al.addIfAbsent(e);
    }
/**
     * 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 <tt>addAll</tt> 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 <tt>true</tt> 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) {
        return al.addAllAbsent(c) > 0;
    }

As can be seen from the above, CopyOnWriteArraySet adds elements by calling the addIfAbsent(e) and addAllAbsent(c) methods of CopyOnWriteArrayList to add elements. These methods are to determine whether the added element already exists in the array in the container when adding an element. Go back to the source code of CopyOnWriteArrayList, as follows:

/**
     * Append the element if not present.
     *
     * @param e element to be added to this list, if absent
     * @return <tt>true</tt> if the element was added
     */
    public boolean addIfAbsent(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            // Copy while checking if already present.
            // This wins in the most common case where it is not present
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = new Object[len + 1];
            for (int i = 0; i < len; ++i) {
                if (eq(e, elements[i]))
                    return false; // exit, throwing away copy
                else
                    newElements[i] = elements[i];
            }
            newElements [len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

When adding elements, a new array is created, and then each element is traversed to see if the object to be added is equal to the existing object in the array, if so, it returns directly to failure, otherwise the elements in the old array are assigned to the new array and added new element and returns success. Since the whole operation involves the change of the elements of the array, the whole process is done under the protection of the lock.

For the addition of collection elements, first determine whether there are new elements in the collection, if so, create a new array, and then copy the elements of the original array and the elements that do not exist in the new collection to the new array by the System.arraycopy method. . As follows:

/**
     * 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;
        Object[] uniq = new Object[cs.length];
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            int added = 0;
            for (int i = 0; i < cs.length; ++i) { // scan for duplicates
                Object e = cs[i];
                if (indexOf(e, elements, 0, len) < 0 &&
                    indexOf(e, uniq, 0, added) < 0)
                    uniq[added++] = e;
            }
            if (added > 0) {
                Object[] newElements = Arrays.copyOf(elements, len + added);
                System.arraycopy(uniq, 0, newElements, len, added);
                setArray(newElements);
            }
            return added;
        } finally {
            lock.unlock();
        }
    }

As for the acquisition of elements, in fact, like CopyOnWriteArrayList, there is no concurrency problem, as long as the elements are acquired and returned according to the array index.

Generally speaking, this container does not have much logic. Based on its internal implementation, it actually has the same concurrent write performance problem as CopyOnWriteArrayList, so it is also suitable for occasions where more reads and writes less.

Thank you for reading. If you are interested in Java programming, middleware, databases, and various open source frameworks, please pay attention to my blog and Toutiao (Source Code Empire). The blog and Toutiao will regularly provide some related technical articles for later. Let's discuss and learn together, thank you.

If you think the article is helpful to you, please give me a reward. A penny is not too little, and a hundred is not too much. ^_^ Thank you.





Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325915093&siteId=291194637