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