Thread safety of JUC collection

List collection thread unsafe operation

Collection thread unsafe case

package com.atguigu.test;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
 * Collection thread safety case
 */
    public class NotSafeDemo {
    /**
     * Multiple threads modify the collection at the same time 
     * @param args
     */
    public static void main(String[] args) {
        List list = new ArrayList();
        for (int i = 0; i < 100; i++) {
            new Thread(() ->{
                list.add(UUID.randomUUID().toString());
                System.out.println(list);
            }, "thread " + i).start();
        }
    }
}

Abnormal content
java.util.ConcurrentModificationException
Question: why do concurrent modification exceptions occur?
View the source code of the add method of ArrayList

public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    // Increments modCount!! elementData[size++] = e;
    return true;
}

So how do we solve the thread safety problem of List type?

Vector

Vector is a vector queue, which is a class added in JDK1.0.

  • It inherits from AbstractList and implements list, randomaccess and clonable interfaces.
  • Vector inherits AbstractList and implements List; Therefore, it is a queue that supports related functions such as addition, deletion, modification and traversal.
  • Vector implements RandmoAccess interface, which provides random access function.
  • RandmoAccess is implemented by List in java to provide fast access for List.
  • In Vector, we can quickly obtain the element object through the element serial number; This is fast random access.
    Vector implements the clonable interface, that is, the clone() function. It can be cloned.
  • Unlike ArrayList, operations in Vector are thread safe.

List collection thread safety case 1

NotSafeDemo code modification

package com.atguigu.test;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
/**
 * Collection thread safety case
 */
public class NotSafeDemo {
    /**
     * Multiple threads modify the collection at the same time 
     * @param args
     */
    public static void main(String[] args) {
        List list = new Vector();
        for (int i = 0; i < 100; i++) {
            new Thread(() ->{
                list.add(UUID.randomUUID().toString());
                System.out.println(list);
            }, "thread " + i).start();
        }
    }
}

There is no concurrency exception in the running. Why?

View the add method of Vector

public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

The add method is synchronized, thread safe!
Therefore, there are no concurrent exceptions

Collections

Collections provides the method synchronizedList
Ensure that the list is thread safe for synchronization

List collection thread safety case 2

NotSafeDemo code modification

package com.atguigu.test;
import java.util.*;
/**
 * Collection thread safety case
 */
public class NotSafeDemo {
    /**
     * Multiple threads modify the collection at the same time 
     * @param args
     */
    public static void main(String[] args) {
        List list = Collections.synchronizedList(new ArrayList<>());
        for (int i = 0; i < 100; i++) {
            new Thread(() ->{
                list.add(UUID.randomUUID().toString());
                System.out.println(list);
            }, "thread " + i).start();
        }
    }
}

No concurrent modification exception
View method source code

public static <T> List<T> synchronizedList(List<T> list) {
    return ( list instanceof RandomAccess ?
        new SynchronizedRandomAccessList<>(list) :
        new SynchronizedList<>(list));
}

Copyonwritearraylist (key)

CopyOnWriteArrayList is equivalent to thread safe ArrayList.
Like ArrayList, it is a variable array;
However, unlike ArrayList, it has the following features:

  • It is best suited for applications that:
    The List size is usually kept small, and read-only operations are much more than variable operations. It is necessary to prevent conflicts between threads during traversal.
  • It is thread safe.
  • Because you usually need to copy the entire underlying array, variable operations (add(), set(), remove(), and so on) are expensive.
  • Iterators support immutable operations such as hasNext(), next(), but do not support immutable operations such as remove().
  • Traversal using iterators is fast and does not conflict with other threads. When constructing iterators, iterators rely on invariant array snapshots.

Idea and principle of CopyOnWriteArrayList

  • Low efficiency of exclusive lock: it is solved by the idea of separation of read and write
  • The write thread obtains the lock, and other write threads are blocked
  • Copy ideas:
  1. When we add elements to a container, we do not directly add them to the current container, but Copy the current container first.
  2. Copy a new container, and then add elements to the new container. After adding elements, point the reference of the original container to the new container.
  3. At this time, a new problem will be thrown out, that is, the problem of inconsistent data. If the write thread has not had time to write back to memory, other threads will read dirty data.
    This is the idea and principle of CopyOnWriteArrayList. Just a copy.

List collection thread safety case 3

NotSafeDemo code modification

package com.atguigu.test;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
 * Collection thread safety case
 */
public class NotSafeDemo {
    /**
     * Multiple threads modify the collection at the same time * @ param args
     */
    public static void main(String[] args) {
        List list = new CopyOnWriteArrayList();
        for (int i = 0; i < 100; i++) {
            new Thread(() ->{
                list.add(UUID.randomUUID().toString());
                System.out.println(list);
            }, "thread " + i).start();
        }
    }
}

No thread safety problem cause analysis (key points):

Dynamic array and thread safety
Next, the principle of CopyOnWriteArrayList is further explained from the two aspects of "dynamic array" and "thread safety".

  • Dynamic array mechanism

    • It has a "volatile array" inside to hold data. When "adding / modifying / deleting" data, a new array will be created, and the updated data will be copied to the new array. Finally, the array will be assigned to "volatile array", which is why it is called CopyOnWriteArrayList
    • Because it creates a new array when "adding / modifying / deleting" data, it involves the operation of modifying data,
    • CopyOnWriteArrayList is inefficient; However, it is more efficient to perform only traversal search.
  • "Thread safety" mechanism

    • Implemented through volatile and mutex.
    • Use the "volatile array" to save the data. When a thread reads the volatile array, it can always see the last write of the volatile variable by other threads; In this way, volatile provides the guarantee of the mechanism that "the data read is always up-to-date".
    • Protect data through mutexes. When "adding / modifying / deleting" data, you will first "obtain the mutex", and then update the data to the "volatile array" after modification, and then "release the mutex", so as to achieve the purpose of protecting data.

Set case DEMO

package com.java.juc.lock;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
public class ThreadDemo4 {

    public static void main(String[] args) {
        //list collection thread is unsafe
        //listDemo();
        //HashSet collection thread unsafe demo
        //setDemo();
        
        /**
         * ConcurrentModificationException Concurrent modification exception
         */
        //Map<String,String > map = new HashMap<>();
        Map<String,String > map = new ConcurrentHashMap<>();
        for(int i=0;i<30;i++){
            String key = String.valueOf(i);
            new Thread(()->{
                //Add content to collection
                map.put(key,UUID.randomUUID().toString().substring(0,8));
                //Get content from collection
                System.out.println(map);
            },"Thread:" + key).start();

        }

    }

    /**
     * HashSet Collection thread unsafe
     */
    private static void setDemo(){
        /**
         * ConcurrentModificationException Concurrent modification exception
         */
        //Set<String> set = new HashSet<>();
        Set<String> set = new CopyOnWriteArraySet<>();
        for(int i=0;i<30;i++){
            new Thread(()->{
                //Add content to collection
                set.add(UUID.randomUUID().toString().substring(0,8));
                //Get content from collection
                System.out.println(set);
            },"Thread:"+ String.valueOf(i)).start();

        }
    }
    /**
     * list Collection thread unsafe
     */
    private static void listDemo(){
        /**
         *  ConcurrentModificationException Concurrent modification exception
         */
        // List<String> list = new ArrayList();
        /**
         * Solution 1: Vector
         */
        // List<String> list = new Vector<>();

        /**
         * Solution 2: Collections.synchronizedList
         */
        //List<String> list = Collections.synchronizedList(new ArrayList<>());

        /**
         * Solution 3: CopyOnWriteArrayList
         */
        List<String> list = new CopyOnWriteArrayList<>();

        for(int i=0;i<10;i++){
            new Thread(()->{
                //Add content to collection
                list.add(UUID.randomUUID().toString().substring(0,8));

                //Get content from collection
                System.out.println(list);

            },"Thread:"+ String.valueOf(i)).start();

        }
    }
    
}

Summary (key points)

1. Thread safe and thread unsafe collection
There are two types of collection types: thread safe and thread unsafe,
Common examples:
ArrayList ----- Vector
HashMap -----HashTable
However, the above are implemented through the synchronized keyword, which is inefficient

2. Thread safe collections built by collections
3.java.util.concurrent
CopyOnWriteArrayList CopyOnWriteArraySet type ensures thread safety through dynamic array and thread safety

Tags: Java JUC

Posted on Thu, 28 Oct 2021 05:07:04 -0400 by petitduc