Insert a picture description here

The introduction

Interviewer: Young man, you look familiar. Did you come here for an interview last year? B: Oh, no. This is my first time here. Interviewer: Ok, let’s start today’s interview. Let’s start with something simple. What do you know about Java containers? Well, common Java containers are ArrayList(thread-safe), HashMap (thread-safe), HashSet (thread-safe), and ConcurrentHashMap (thread-safe). Is there a thread-safe ArrayList column? Fat Two: Well… I think I’ve got a knowledge blind spot. Interviewer: That’s all for today’s interview. I will have another meeting later. I will contact you if there is any notice. The above story is pure fiction, if any similar, please use this article as the main.

What is a COW

In Java, when it comes to collection containers, we usually think of HashMap, ArrayList and HasHSet, which are also the most commonly used containers in daily development. These are all non-thread-safe, so if we have a particular business that needs to use thread-safe container columns,

  • HashMapYou can useConcurrentHashMapInstead.
  • ArrayListYou can useCollections.synchronizedList()Methods (listUse every methodsynchronizedModify) or useVector(I don’t use it anymore. I use every methodsynchronizedModify) or useCopyOnWriteArrayListAlternative.
  • HasHSet can be usedCollections.synchronizedSetOr useCopyOnWriteArraySetInstead. CopyOnWriteArraySet why not CopyOnWriteHashSet becauseCopyOnWriteArraySetThe underlying is the adoption ofCopyOnWriteArrayListWe can see thatCopyOnWriteArrayListOccurs multiple times in thread-safe containers. First of all, what isCopyOnWrite?Copy-On-WriteReferred to as”COW, is an optimization strategy used in programming.

A CopyOnWrite container is a container for copying while writing. The common understanding is that when we add elements to a container, we do not directly add to the current container, but first Copy the current container, Copy the new container, then add elements to the new container, after adding elements, the original container reference to the new container. The advantage of this is that we can do concurrent reads to the CopyOnWrite container without locking, since no elements are being added to the current container. So the CopyOnWrite container is also an idea of read-write separation, reading and writing different containers.

Why was COW introduced

To prevent abnormal ConcurrentModificationException

In Java if we adopt the cycle of incorrect posture to traverse the List, if thrown while traversing the modify Java util. ConcurrentModificationException wrong. If you’re not familiar with iterating through an ArrayList, check out this article, “Do you know how to delete an ArrayList?”

        List<String> list = new ArrayList<>();

        list.add("Zhang");

        list.add("Java financial");

        list.add("javajr.cn");

        Iterator<String> iterator = list.iterator();

        while(iterator.hasNext()){

            String content = iterator.next();

            if("Zhang".equals(content)) {

                list.remove(content);

            }



        }

Copy the code

The chestnut occur above Java. Util. ConcurrentModificationException abnormal, if change the ArrayList to CopyOnWriteArrayList abnormal is not going to happen.

Thread-safe containers

One thread adds data to the List, and another thread loops through the List to read data.

    List<String> list = new ArrayList<>();

        list.add("Zhang");

        list.add("Java financial");

        list.add("javajr.cn");

        Thread t = new Thread(new Runnable() {

            int count = 0;

            @Override

            public void run(a) {

                while (true) {

                    list.add(count++ + "");

                }

            }

        });

        t.start();

        Thread.sleep(10000);

        for (String s : list) {

            System.out.println(s);

        }

Copy the code

We run the above code will occur ConcurrentModificationException abnormalities, if changed the ArrayList CopyOnWriteArrayList is everything is ok.

The realization of the CopyOnWriteArrayList

CopyOnWriteArrayList is thread-safe, so let’s take a look at how CopyOnWriteArrayList is thread-safe.

public class CopyOnWriteArrayList<E>

    implements List<E>, RandomAccess.Cloneable.java.io.Serializable 
{

    private static final long serialVersionUID = 8673264195747942595L;



    /** The lock protecting all mutators */

    final transient ReentrantLock lock = new ReentrantLock();



    /** The array, accessed only via getArray/setArray. */

    private transient volatile Object[] array;

Copy the code

CopyOnWriteArrayList and ArrayList are both implemented using an array of objects, but the array of CopyOnWriteArrayList is volatile. For advice on why volatile modifiers are needed, see “Does Java’s Synchronized Prevent Instruction Reordering?” ReentrantLock has also been added.

The add method:

    public boolean add(E e) {

        // Get the lock first

        final ReentrantLock lock = this.lock;

        lock.lock();

        try {

            Object[] elements = getArray();

            int len = elements.length;

            // Copy a new array

            Object[] newElements = Arrays.copyOf(elements, len + 1);

            newElements[len] = e;

            // Assign the value of the new array to the original array

            setArray(newElements);

            return true;

        } finally {

            / / releases the lock

            lock.unlock();

        }

    }

Copy the code

The above source code we can find relatively simple, there are a few points need to pay a little attention to

  • The time to add data is throughReentrantLockLock operation to (injdk11“Was adoptedsynchronizedTo replaceReentrantLockMake sure that only one thread copies the array when multithreading writes, otherwise there will be multiple copies of the copied data in memory, resulting in data corruption.
  • Arrays are passed throughvolatileAccording tovolatilehappens-beforeRule: Changes made by the writer thread to an array reference are immediately visible to the reader thread.
  • Copy-on-write ensures that reads and writes operate in two different data containers.

Implement a COW container yourself

Java and send package provides two concurrent containers using CopyOnWrite mechanism, they are CopyOnWriteArrayList and CopyOnWriteArraySet, But there is no CopyOnWriteHashMap. We can implement a CopyOnWriteHashMap ourselves

public class CopyOnWriteHashMap<K.Vimplements Map<K.V>, Cloneable {



    final transient ReentrantLock lock = new ReentrantLock();



    private volatile Map<K, V> map;





    public CopyOnWriteHashMap(a) {

        map = new HashMap<>();

    }



    @Override

    public V put(K key, V value) {

        final ReentrantLock lock = this.lock;

        lock.lock();

        try {

            Map<K, V> newMap = new HashMap<K, V>(map);

            V val = newMap.put(key, value);

            map = newMap;

            return val;

        } finally {

            lock.unlock();

        }

    }



    @Override

    public V get(Object key) {

        return map.get(key);

    }

    @Override

    public V remove(Object key) {

        final ReentrantLock lock = this.lock;

        lock.lock();

        try {

            Map<K, V> newMap = new HashMap<K, V>(map);



            if(! newMap.containsKey(key)) {

                return null;

            }

            V v = newMap.get(key);

            newMap.remove(key);

            map = newMap;

            return v;

        }finally {

            lock.unlock();

        }

    }

Copy the code

Above, we have implemented a simple CopyOnWriteHashMap, which only implements add, remove and get methods, and other methods can be implemented by themselves. As long as the data changes, locks will be added, while reading does not need to be locked.

Application scenarios

The CopyOnWrite concurrent container is suitable for concurrent scenarios in which there are many reads and few writes, such as the blacklist and whitelist, basic data caching of countries and cities, and system configuration. These are basically just want to start the project at the time of initialization, change frequency is very low. If a Vector is used for this read-much, read-little scenario, the collections-wrapped approach doesn’t make sense, because although multiple readers are reading from the same data container, the reader thread doesn’t modify the data in the container, so it doesn’t need to lock the read.

CopyOnWrite shortcomings

CopyOnWriteArrayList is a thread-safe version of ArrayList, but it makes a copy of the data every time it changes data, so CopyOnWriteArrayList is only suitable for read more than write less or no-lock read scenarios. If we use CopyOnWriteArrayList in a real business, it must be because the scenario fits, not because it shows off.

Memory usage problem

Since CopyOnWrite’s copy-on-write mechanism has two array objects in memory for each write operation, if the array object occupies a large amount of memory, frequent writes will result in frequent Yong and Full GC.

Data consistency issues

The CopyOnWrite container only guarantees final data consistency, not real-time data consistency. The reading thread may not immediately read the newly modified data because the modification takes place on the replica. But eventually the modification is done and the container is updated so this is the final consistency.

CopyOnWriteArrayList and Collections. SynchronizedList ()

Under the simple test the CopyOnWriteArrayList and Collections. SynchronizedList found () to read and write:

  • Write in high concurrency CopyOnWriteArray than synchronous Collections. SynchronizedList one hundred times slower
  • In the high performance of concurrent read-access CopyOnWriteArray than synchronous Collections. SynchronizedList dozens of times faster.
  • Why is CopyOnWriteArrayList so slow for high concurrency writes? Because its Arrays. CopyOf creates new Arrays every time it adds, frequent add consumes a lot of memory requisition and release performance.
  • When high concurrent read CopyOnWriteArray unlocked, Collections. SynchronizedList lock so read with low efficiency.

conclusion

When you select CopyOnWriteArrayList you must read more than you write. If reading and writing about the same suggestion choice Collections. SynchronizedList.

The end of the

  • As a result of their talent and learning, it is inevitable that there will be mistakes, if you found the wrong place, but also hope that the message to me pointed out, I will correct it.
  • If you think the article is good, your forwarding, sharing, appreciation, like, message is the biggest encouragement to me.
  • Thank you for reading. Welcome and thank you for your attention.