scene
Because when working, you don't over-consider the issue of high concurrency. Most collections use normal lists, sets, maps. There's not much problem either. But if you're in a multi-threaded scenario where multiple threads are manipulating a collection at the same time, there's a lot of problem.
Collection Security Issue Code Display
List
Here's a piece of code
List<String> list = new ArrayList<>(); for (int i = 0; i < 300; i++) { new Thread(() -> { list.add(UUID.randomUUID().toString().substring(10)); System.out.println(list); }).start(); }
This is a partial execution result
java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911) at java.util.ArrayList$Itr.next(ArrayList.java:861) at java.util.AbstractCollection.toString(AbstractCollection.java:461) at java.lang.String.valueOf(String.java:2994) at java.io.PrintStream.println(PrintStream.java:821) at com.anxin.juc.listThread.ListThread.lambda$main$0(ListThread.java:20) at java.lang.Thread.run(Thread.java:748) java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911) at java.util.ArrayList$Itr.next(ArrayList.java:861) at java.util.AbstractCollection.toString(AbstractCollection.java:461) at java.lang.String.valueOf(String.java:2994) at java.io.PrintStream.println(PrintStream.java:821) at com.anxin.juc.listThread.ListThread.lambda$main$0(ListThread.java:20) at java.lang.Thread.run(Thread.java:748) java.util.ConcurrentModificationException
For ConcurrentModificationException exceptions, which means concurrent modification exceptions, this exception pops up when we print this collection, including sometimes when we delete while traversing, which is resolved by an iterator. Drag away.
Then how to solve this problem in JUC. There are almost three solutions.
1. Using Vector Collection
List<String> list = new Vector<>(); for (int i = 0; i < 300; i++) { new Thread(() -> { list.add(UUID.randomUUID().toString().substring(10)); System.out.println(list); }).start(); }
This Vector is a thread-safe, single-threaded collection. It solves this problem. Almost all of his methods are decorated with synchronous locks. But this has not been used since I worked in this field, and is obsolete because of its low performance. And synchronous locks are not really used in some very concurrent scenarios.
2. Use Collections.synchronizedList
List<String> list = Collections.synchronizedList(new ArrayList<>()); for (int i = 0; i < 300; i++) { new Thread(() -> { list.add(UUID.randomUUID().toString().substring(10)); System.out.println(list); }).start(); }
The source code is as follows:
public static <T> List<T> synchronizedList(List<T> list) { return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<T>(list) : new SynchronizedList<T>(list)); }
It is not difficult to explain why he is thread safe.
3. Use CopyOnWriteArrayList
List<String> list =new CopyOnWriteArrayList(); for (int i = 0; i < 300; i++) { new Thread(() -> { list.add(UUID.randomUUID().toString().substring(10)); System.out.println(list); }).start(); }
This is mainly achieved by write-time replication technology.
Let's look at a code and guess if it will go wrong?
List<String> list =new CopyOnWriteArrayList(); list.add("111"); list.add("222"); list.add("333"); list.add("444"); list.add("555"); for (String s : list) { list.remove(s); System.out.println(list); }
No error! Why?
because
When we add elements to a container, instead of directly adding them to the current container, we first Copy the current container, duplicate a new container, then add elements to the new container, and then point the reference of the original container to the new container.
A new problem, data inconsistency, is thrown. If the writing thread has not had time to write in memory, other threads will read dirty data. ==This is the idea and principle of CopyOnWriteArrayList. It is a copy.
- It is best suited for applications with the following characteristics: List sizes are usually kept small, and read-only operations are much more
For mutable operations, conflicts between threads need to be prevented during traversal. - It is thread safe.
- Variable operations (add(), set(), and remove() are usually required to copy the entire base array.
Etc.) is very expensive. - Iterators support immutable operations such as hasNext(), next(), but not variable remove().
- Iterators are fast to traverse and do not conflict with other threads. Iterations are constructed
Iterators depend on a constant array snapshot when using the. - Low efficiency of exclusive locks: using read-write separation to solve
- Write thread acquired lock, other write threads blocked
- Copy Ideas
Set
Set set = new CopyOnWriteArraySet<>();
Solve the problem
Map
HashTable
Map<String,String> map = new Hashtable<>(); for (int i = 0; i < 300; i++) { new Thread(() -> { map.put(UUID.randomUUID().toString().substring(8),UUID.randomUUID().toString().substring(16)); System.out.println(map); }).start(); }
It is important to mention hashtable, the underlying array + chain table implementation, whether key or value cannot be null, thread-safe. Thread-safe is achieved by locking the entire HashTable while modifying data, which is not very efficient and is not recommended.
ConcurrentHashMap
Map<String,String> map = new ConcurrentHashMap<>(); for (int i = 0; i < 300; i++) { new Thread(() -> { map.put(UUID.randomUUID().toString().substring(8),UUID.randomUUID().toString().substring(16)); System.out.println(map); }).start(); }
This is recommended, so why is it recommended? You must first understand the question, why is the collection thread safe?
ConcurrentHashMap is made up of one Segment array and multiple HashEntry arrays
Segment s are re-entrantLocks that play the role of locks in ConcurrentHashMap; HashEntry stores key-value pairs of data.
So why is he efficient?
That's why I said earlier that synchronous locks can't carry a lot of concurrency at all. Because HashTable s are the stream where many threads compete for a lock. ConcurrentHashMap locks are very small in size, with one lock per segment. So they are efficient.
summary
1. Thread security and incomplete thread integration
There are two types of collection types, thread-safe and thread-insecure, common examples are:
ArrayList ----- Vector
HashMap -----HashTable
However, these are all implemented by synchronized keywords, which are inefficient
2.Collections Build Thread Security Collection
3.java.util.concurrent under concurrent package
The CopyOnWriteArrayList CopyOnWriteArraySet type ensures thread security through dynamic arrays and thread security.
Forecast
The next section is the volatile keyword.