Unsafe solution of Java collection class under multithreading

Keywords: Java Concurrent Programming set

2, Collection class unsafe

2.1ArrayList thread is unsafe

2.1.1 example

Single thread

public class NotSafeDemo {
    //Single thread is safe
    public static void main(String[] args) {
        List<String> list = Arrays.asList("a", "b", "c");
        list.forEach(System.out::println);
    }
}

a
b
c

Multithreading

public class NotSafeDemo {
    //Multithreading is unsafe
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}
//Error reported: java.util.ConcurrentModificationException concurrent modification exception

The reason why ArrayList reports an error under multithreading is that ArrayList is a thread unsafe class. It can be seen from the source code that its add method is not locked, which means that multiple threads can operate the resource class at the same time, which is unsafe

2.1.2 solving the multi thread insecurity of ArrayList

Method 1: use Vector class (not recommended)

public class NotSafeDemo {
    //Multithreading is unsafe
    public static void main(String[] args) {
        List<String> list = new Vector<>();
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

Through the source code, we can see that the add method of Vector class is locked, so it is safe

Method 2: Collections tool class

public class NotSafeDemo {
    //Multithreading is unsafe
    public static void main(String[] args) {
        List<String> list =  Collections.synchronizedList(new ArrayList<String>());
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

Method 3: use CopyOnWriteArrayList class in JUC (recommended)

CopyOnWriteArrayList is in the java.util.concurrent package

public class NotSafeDemo {
    //Multithreading is unsafe
    public static void main(String[] args) {
          List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

In addition, there are many commonly used thread safe classes in the CopyOnWriteArrayList package

Why can CopyOnWriteArrayList solve thread safety problems?

From the source code, we can see that the add method of CopyOnWriteArrayList adds a reentrant lock

CopyOnWrite idea:

  • The idea of copy on write (COW) is an optimization strategy in the field of computer programming. The core idea is that if multiple Callers require the same resource (such as memory or data storage on disk) at the same time, they will jointly obtain the same pointer to the same resource. The system will not really copy a private copy to the caller until a caller's view modifies the resource content, The original resources seen by other Callers remain unchanged. This process is transparent to other Callers. The main advantage of this method is that if the caller does not modify the resource, no private copy will be created. Therefore, multiple Callers can share the same resource only during the read operation. Separate reading and writing. When writing, copy a new array, and assign the new array to array after completing the insertion, modification or removal operations

Why is CopyOnWriteArrayList concurrency safe and better than Vector

  • Vector is a method of adding, deleting, modifying and querying. It is synchronized to ensure synchronization. However, each method needs to obtain a lock when it is executed, and the performance will be greatly reduced. CopyOnWriteArrayList only locks the addition, deletion and modification, but does not lock the reading. Its performance in reading is better than vector. CopyOnWriteArrayList supports the concurrency of more reads and less writes.

2.2 HashSet thread unsafe

2.2.1 example

public class NotSafeDemo {
    //Multithreading is unsafe
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

2.2.2 solving the insecurity of HashSet multithreading

Method 1: Collections tool class

public class NotSafeDemo {
    //Multithreading is unsafe
    public static void main(String[] args) {
        Set<String> set = Collections.synchronizedSet(new HashSet<>());
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

Method 2: use CopyOnWriteArraySet class

public class NotSafeDemo {
    //Multithreading is unsafe
    public static void main(String[] args) {
        CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

2.3 HashMap thread is unsafe

2.3.1 example

public class NotSafeDemo {
    //Multithreading is unsafe
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

2.3.2 solving the insecurity of HashMap multithreading

Method 1: Collections tool class

public class NotSafeDemo {
    //Multithreading is unsafe
    public static void main(String[] args) {
        Map<String, String> map = Collections.synchronizedMap(new HashMap<String, String>());
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

Method 2: use the ConcurrentHashMap class

public class NotSafeDemo {
    //Multithreading is unsafe
    public static void main(String[] args) {
        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

Posted by coffeehead on Sat, 25 Sep 2021 04:51:09 -0700