Thinking logic of computer programs (73) and thinking 73

Source: Internet
Author: User
Tags concurrentmodificationexception

Thinking logic of computer programs (73) and thinking 73

In this section and the following sections, we will discuss the container classes in Java and package. This section describes two simple classes, CopyOnWriteArrayList and CopyOnWriteArraySet, and discusses their usage and implementation principles. Their usage is relatively simple. What we need to understand is their implementation mechanism: Copy-On-Write, that is, Copy at Write or Write, this is an important way to solve the concurrency problem.

CopyOnWriteArrayList

Basic usage

CopyOnWriteArrayList implements the List interface. Its usage is basically the same as that of other lists, such as ArrayList. The difference is:

  • It is thread-safe and can be accessed by multiple threads concurrently.
  • Its iterator does not support modification, but does not throw ConcurrentModificationException.
  • It supports atomic composite operations

We have mentioned several synchronization container issues based on synchronized in section 66. During iteration, You need to lock the entire list object. Otherwise, ConcurrentModificationException will be thrown. CopyOnWriteArrayList does not have this problem, and no locks are required during iteration. In section 66, the sample code is:

public static void main(String[] args) {    final List<String> list = Collections            .synchronizedList(new ArrayList<String>());    startIteratorThread(list);    startModifyThread(list);}

Replace list with CopyOnWriteArrayList, and no exception will occur, for example:

public static void main(String[] args) {    final List<String> list = new CopyOnWriteArrayList<>();    startIteratorThread(list);    startModifyThread(list);}

However, it should be noted that in the implementation before Java 1.8, the CopyOnWriteArrayList iterator does not support modification or some operations that depend on the iterator to modify methods, for example, the sort method of Collections. Let's look at an example:

public static void sort(){    CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();    list.add("c");    list.add("a");    list.add("b");    Collections.sort(list);}

Executing this code throws an exception:

Exception in thread "main" java.lang.UnsupportedOperationException    at java.util.concurrent.CopyOnWriteArrayList$COWIterator.set(CopyOnWriteArrayList.java:1049)    at java.util.Collections.sort(Collections.java:159)

Why? Because the Collections. sort method depends on the set Method of the iterator, the code is:

public static <T extends Comparable<? super T>> void sort(List<T> list) {    Object[] a = list.toArray();    Arrays.sort(a);    ListIterator<T> i = list.listIterator();    for (int j=0; j<a.length; j++) {        i.next();        i.set((T)a[j]);    }}

Another problem with synchronous containers based on synchronized is compound operations. For example, the caller also needs to lock the synchronization containers before checking and updating. CopyOnWriteArrayList directly supports two atomic methods:

// If not, true is returned. Otherwise, falsepublic boolean addIfAbsent (E e) is returned. // if not, non-repeated elements in c are added in batches, returns the number of actually added public int addAllAbsent (Collection <? Extends E> c)

Basic Principles

CopyOnWriteArrayList is also an array, but this array is updated as a whole in an atomic manner. Each modification operation creates an array, copies the content of the original array to the new array, modifies the new array, and sets the internal array reference in an atomic manner, this is the copy at write time.

All read operations first obtain the currently referenced array and then directly access the array. During reading, the internal array reference may have been modified, but the read operation will not be affected, it still accesses the original array content.

In other words, the array content is read-only, and write operations are implemented by creating an array and modifying array references atomically. Let's take a look at the code.

The internal array declaration is:

private volatile transient Object[] array;

Note that it is declared as volatile, which is required to ensure memory visibility. After the write operation is changed, the read operation can be seen. There are two ways to access/set the array:

final Object[] getArray() {    return array;}final void setArray(Object[] a) {    array = a;}

In the CopyOnWriteArrayList, the read operation does not require a lock, but can be parallel. The read and write operations can also be parallel. However, multiple threads cannot write data at the same time. Each write operation must obtain the lock first, copyOnWriteArrayList uses ReentrantLock internally and the member declaration is:

transient final ReentrantLock lock = new ReentrantLock();

The default constructor is:

public CopyOnWriteArrayList() {    setArray(new Object[0]);} 

An empty array is set.

The code for the add method is:

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();    }}

The Code is also easy to understand. The add method is a modification operation, and the entire process needs to be locked. First, we get the current array elements, and then copy a new array newElements with a length plus 1, add elements to the new array, and call setArray to modify the internal array reference atomicity.

The code for searching the element indexOf is:

public int indexOf(Object o) {    Object[] elements = getArray();    return indexOf(o, elements, 0, elements.length);}

Obtain the current array elements first, and then call another indexOf for search. The Code is as follows:

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;    }    return -1;}

All data accessed by the indexOf method is passed in through parameters, and the array content is not modified, so there is no concurrency problem.

The iterator method is:

public Iterator<E> iterator() {    return new COWIterator<E>(getArray(), 0);}

COWIterator is an internal class that is passed to it as an unchanged array. It only reads the array and does not support modification.

The implementation of other methods is similar, so we will not go into details.

Summary

Each modification creates a new array and copies all the content. This sounds unacceptable. If the array is large and the modification operation is frequent, you can imagine it, copyOnWriteArrayList has low performance. Indeed, CopyOnWriteArrayList is not applicable to scenarios with large arrays and frequent changes. It aims to optimize read operations. The read does not need to be synchronized, and the performance is very high. However, it sacrifices the write performance while optimizing read operations.

We have introduced two ways to ensure thread security: Lock, synchronized or ReentrantLock, and loop CAS, copying During writing reflects another way of thinking to ensure thread security. For the vast majority of access requests, and there are a large number of concurrent threads that require reading, only a few threads write, and only occasionally write, this copy-to-write is a good solution.

Copy-on-write is an important concept used in various computer programs. For example, it is often used for process management and memory management within the operating system. In process management, sub-processes often share the resources of the parent process, and are only copied at write time. In memory management, when multiple programs access the same file at the same time, the operating system may only load one copy in the memory and only copy the copy when the program is to be written, allocate your own memory, and the copy may not be all copied. Instead, it only copies the page where the write location is located. The page is a unit of memory managed by the operating system. The specific size is related to the system, the typical size is 4 kb.

CopyOnWriteArraySet

CopyOnWriteArraySet implements the Set interface and does not contain duplicate elements. It is easy to use and we will not go into details. Internally, it is implemented through CopyOnWriteArrayList, and its member declaration is:

private final CopyOnWriteArrayList<E> al;

Initialized in the constructor, such:

public CopyOnWriteArraySet() {    al = new CopyOnWriteArrayList<E>();}

The add method code is:

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

The addIfAbsent method of CopyOnWriteArrayList is called.

The contains method code is:

public boolean contains(Object o) {    return al.contains(o);}

Because CopyOnWriteArraySet is implemented based on CopyOnWriteArrayList, compared with the Implementation classes of Set, such as HashSet and TreeSet, it has low performance and is not applicable to collections with many elements. If the number of elements is large, you can consider ConcurrentHashMap or ConcurrentSkipListSet. The two classes are described in the following sections.

ConcurrentHashMap is similar to HashMap. It is applicable to scenarios that do not require sorting. ConcurrentSkipListSet is similar to TreeSet and is applicable to scenarios that require sorting. Java concurrent package does not have a concurrent container corresponding to HashSet, but it is easy to build a container Based on ConcurrentHashMap and use the Collections. newSetFromMap method.

Summary

This section describes the CopyOnWriteArrayList and CopyOnWriteArraySet, including their usage methods and principles. They are suitable for reading far more data than writing data and the collection is not big, this is an Important Thought and technology in computer programs.

In the next section, we will discuss an important concurrent container-ConcurrentHashMap.

(As in other sections, all the code in this section is in the https://github.com/swiftma/program-logic)

----------------

For more information, see the latest article. Please pay attention to the Public Account "lauma says programming" (scan the QR code below), from entry to advanced, ma and you explore the essence of Java programming and computer technology. Retain All copyrights with original intent.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.