preface
Some time ago, a new employee came to the company. He said that the new employee had been outsourcing code for one and a half years before he came to our company. Once I chatted with him about List. I casually asked him how to solve the unsafe multi-threaded concurrency of List collection when outsourcing. Unexpectedly, he looked at me in a confused circle and said whether it was safe? I was speechless and asked him what he was doing after a year and a half. It was not clear. He said that before outsourcing, he was just fooling around almost every day. Only when he felt that he couldn't go on like this would he want to leave his job and change to a better job. I can only say that fortunately, he "woke up" early
In the daily development process, List is a common set. For example, the return value ratio of query database content will be loaded with a set, but will there be security problems under the condition of multithreading concurrency? If there is a problem, let's test and solve it
1, The List collection uses simulated concurrency tests
1. Under single thread environment
public static void main(String[] args) { // List collection List<String> list = new ArrayList<>(); // Circular insertion for (int i = 0; i < 10; i++) { list.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(list); } }
We can see that under the condition of single thread, we have no problem in inserting the list. Let's simulate the execution under the condition of concurrency.
2 multithreading environment
public static void main(String[] args) { // List collection List<String> list = new ArrayList<>(); // Circular insertion for (int i = 0; i < 10; i++) { // Start thread execution new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(list); },"thread List").start(); } }
If the ArrayList is modified at the same time during iteration, it will throw Java util. The concurrent modificationexception is a concurrent modification exception.
2, Solution
1. Use Vector class
public static void main(String[] args) { // List collection List<String> list = new Vector<>(); // Circular insertion for (int i = 0; i < 10; i++) { // Start thread execution new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(list); },"thread List").start(); } }
Vector is accessed synchronously. The bottom layer of its add method is decorated with the synchronized keyword.
Test results:
2. Use collections synchronizedList
public static void main(String[] args) { // List collection List<String> list = Collections.synchronizedList(new ArrayList<>()); // Circular insertion for (int i = 0; i < 10; i++) { // Start thread execution new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(list); },"thread List").start(); } }
Looking at the underlying source code, you can find that it also uses the synchronized keyword.
3. Use the concurrent container CopyOnWriteArrayList
public static void main(String[] args) { // List collection List<String> list = new CopyOnWriteArrayList<>(); // Circular insertion for (int i = 0; i < 10; i++) { // Start thread execution new Thread(()->{ list.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(list); },"thread List").start(); } }
Check the source code, which uses the lock lock mechanism.
Copy when writing. When multiple threads call, copy a copy when writing to avoid data problems caused by overwriting. When writing, the original set is not modified, but a new copy is copied. After modification, the pointer is moved.
From jdk1 5 starts Java and provides two concurrent containers implemented by CopyOnWrite mechanism in the contract. They are CopyOnWriteArrayList and CopyOnWriteArraySet. The CopyOnWrite container is very useful and can be used in many concurrent scenarios.
Interpretation of source code:
/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return {@code true} (as specified by {@link Collection#add}) */ public boolean add(E e) { final ReentrantLock lock = this.lock;//Reentrant lock lock.lock();//Lock try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1);//Copy new array newElements[len] = e; setArray(newElements);//Point reference to new array return true; } finally { lock.unlock();//Unlock } }
add() adds a lock when adding a collection to ensure synchronization and avoid copying N copies when multiple threads write.
summary
Usage scenario of CopyOnWriteArrayList: more reading and less writing (blacklist, whitelist, access and update scenarios of commodity categories), and the collection is small. Therefore, generally speaking, we will use the thread safe container provided to us under the JUC package instead of the old generation of thread safe containers.