CopyOnWriteArrayList of Java Concurrent Container

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

CopyOnWriteArrayList is thread safe. As can be seen from the name, it uses the copy-on-write method. In short, when updating the array list, first copy the original array to a new array, then operate on the new array, and finally Points a reference to the original array elements to the new array.

What is Copy-On-Write (COW)? Copy-on-write is an optimization strategy in programming. The basic idea is that everyone shares a piece of content from the beginning, but if someone wants to modify it, then make a new copy and perform it on the new content. Modify, this is a delayed lazy strategy. Starting from JDK1.5, two Copy-On-Write-based containers are provided, one is CopyOnWriteArrayList and the other is CopyOnWriteArraySet.

Let's take a look at how the internals are implemented when adding new elements to this container. (Take JDK1.7 source code as reference)

/**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#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();
        }
    }

As can be seen from the above code, the entire process of adding is carried out under the protection of the ReentrantLock lock. The reason why it needs to be done under the protection of locks is because it involves the generation of new arrays, and locking is to ensure that multiple new array objects will not be generated under concurrent conditions.

There is also a method add(int index, E element) that adds the specified element at the specified index index, as shown below:

/**
     * Inserts the specified element at the specified position in this
     * list. Shifts the element currently at that position (if any) and
     * any subsequent elements to the right (adds one to their indices).
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (index > len || index < 0)
                throw new IndexOutOfBoundsException("Index: "+index+
                                                    ", Size: "+len);
            Object[] newElements;
            int numMoved = len - index;
            if (numMoved == 0)
                newElements = Arrays.copyOf(elements, len + 1);
            else {
                newElements = new Object[len + 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            newElements[index] = element;
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

It can be seen from this method that when adding elements, the index cannot be passed casually, and must be less than or equal to the number of elements in the current array, that is, index<=length(elements), otherwise the exception will be excluded. If the index index is valid, then if the insertion position is the end of the previous element array, you only need to create a new array and assign the last element as the added element. If the insertion position is <length(elements), then it involves the previous element. Position migration, where System.arraycopy is used to copy array elements.

Since all write operations are performed on the basis of the latest array, if there are concurrent writes at this time, they need to be locked for control, but for concurrent reads, this situation is subdivided into the following scenarios:

(a) If the write operation is not completed, then directly read the data of the original array;

(b) If the write operation is completed, but the reference has not yet pointed to the new array, then the original array data is also read;

(c) If the write operation is complete, and the reference already points to the new array, read the data directly from the new array;

From the analysis results of the above scenarios, it is not necessary to lock control in the case of concurrent reading.

After the above analysis, although CopyOnWriteArrayList solves the problem of non-thread safety when using ArrayList under concurrent conditions, it adopts the method of copy-on-write, so there are certain performance problems in writing data under high concurrency conditions.

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=325915108&siteId=291194637