Collection thread safety issues

This is the sixth day of my participation in the First Challenge 2022. For details: First Challenge 2022.

JDK Version:9

Let’s start with what collection thread-safety is: an exception error occurs when multiple threads add or query the same collection.

Examples of recurrence:

package com.JUC;
​
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
​
public class ListSecutity04 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0.8)); System.out.println(list); },String.valueOf(i)).start(); }}}Copy the code

Effect:

Can see the ConcurrentModificationException concurrent modification exception; The problem with this error is that the add method in the ArrayList is not locked.

Check its source code:

public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
    return true;
}
//----------------------------------------
public void add(int index, E element) {
    rangeCheckForAdd(index);
    modCount++;
    final int s;
    Object[] elementData;
    if ((s = size) == (elementData = this.elementData).length)
        elementData = grow();
    System.arraycopy(elementData, index,
                     elementData, index + 1,
                     s - index);
    elementData[index] = element;
    size = s + 1;
}
//----------------------------------------
private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    size = s + 1;
}
Copy the code

Solutions – : Vector and Conllections

As the name suggests, both methods of heading are older. Vector is used because of the sychronized keyword in its add method

public synchronized boolean add(E e) {
    modCount++;
    add(e, elementData, elementCount);
    return true;
}
Copy the code

Another approach is to return the synchronous (thread-safe) Collection supported by the specified Collection by the utility class synchronizedCollection(Collection

C) in the Collection.

Repeat the test and the code passes:

package com.JUC;
​
import java.util.*;
​
public class ListSecutity04 {
    public static void main(String[] args) {
​
// List
      
        list = new ArrayList<>();
      
// List
      
        list = new Vector<>();
      
        List<String> list = Collections.synchronizedList(new ArrayList());
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0.8)); System.out.println(list); },String.valueOf(i)).start(); }}}Copy the code

Although but, we choose CopyOnWriteArrayList;

Solution 2: CopyOnWriteArrayList

Copy-on-write techniques:

List<String> list = new CopyOnWriteArrayList<>();
Copy the code

The idea:

In the case of multi-threading, when the set is written, the system will first copy the original content (A) into (B), the original content (A) can be read concurrently, and the copied content (B) will be written into the new content. When the content is written, A and B will realize overwriting or merging.

The array definitions are volatile, so that each thread can observe the array changes in real time.

private transient volatile Object[] array;
final void setArray(Object[] a) {
    array = a;
}
Copy the code

Add () method source: lock object added lock

final transient Object lock = new Object();
Copy the code
public boolean add(E e) {
    synchronized (lock) {
        Object[] elements = getArray();  // Get the original content
        int len = elements.length;  
        Object[] newElements = Arrays.copyOf(elements, len + 1); // Array copy
        newElements[len] = e;  // Add new content
        setArray(newElements);  // Overwrite the original array
        return true; }}Copy the code

CopyOnWriteArrayList’s biggest feature, read and write separation, final consistency. Better performance than synchronized pessimistic lock. The disadvantage is that replication takes up memory, which may be OOM.

Similarly, thread-unsafe collections include HashMap and HashSet, which have similar solutions in JUC.

We can also carry out code writing, and enter the source code for a simple analysis:

HashSet–CopyOnWriteArraySet

Add method in HashSet (thread unsafe)

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
/ / -- -- -- -- -- -- -- -- -- the map
public V put(K key, V value) {
    return putVal(hash(key), key, value, false.true);
}
Copy the code

Put (map); put (map); put (map);

We can also see that the Set is unordered and not repeated, and that E passed into the Set is passed into the map as a key.

Then take a look at the source of add in CopyOnWriteArraySet:

public boolean add(E e) {
    return al.addIfAbsent(e);  //CopyOnWriteArrayList<E> al = new CopyOnWriteArrayList<E>()
}
//-------------addIfAbsent---------------
public boolean addIfAbsent(E e) {
    Object[] snapshot = getArray();
    return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
    addIfAbsent(e, snapshot);
}
//---------------------addIfAbsent------------------------
private boolean addIfAbsent(E e, Object[] snapshot) {
    synchronized (lock) {
        Object[] current = getArray();
        int len = current.length;
        if(snapshot ! = current) {// Optimize for lost race to another addXXX operation
            int common = Math.min(snapshot.length, len);
            for (int i = 0; i < common; i++)
                if(current[i] ! = snapshot[i] && Objects.equals(e, current[i]))return false;
            if (indexOf(e, current, common, len) >= 0)
                return false;
        }
        Object[] newElements = Arrays.copyOf(current, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    }
Copy the code

As you can see from the source, the set uses CopyOnWriteArrayList, and the data finally added is synchronized in the addIfAbsent method.

HashMap–ConcurrentHashMap

Need not see, I will not, the source code did not understand, temporarily not table.