The following code is executed, how about the program? ?
package VolatilePkg;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
/**
* @author Heian
* @time 19/01/22 10:27
* @copyright(C) 2019 深圳市北辰德科技股份有限公司
* 用途:ArrayList 线程为什么不安全?
*/
public class VolatileTest {
static List<Integer> list = new ArrayList<> ();
//static List<Integer> list = new Vector<> ();
public void add(){
for (int i=0;i<1000;i++){
list.add (1);
}
}
public static void test() throws InterruptedException{
VolatileTest vo = new VolatileTest ();
IntStream.range (0,5).forEach (value -> new Thread (() -> vo.add (),String.valueOf (value)).start ());
while (Thread.activeCount ()>1){
Thread.yield ();
}
System.out.println (list.size ());
}
public static void main(String[] args) throws InterruptedException{
VolatileTest.test ();
}
}
If the thread-safe output is 5000, but output in three ways:
- Less than 5000
- 5000
- Less than 5000 + ArrayIndexOutOfBoundsException
ArrayList can be seen that in the case of multiple threads of operation, can not ensure data integrity. To add source code analysis:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 1
elementData[size++] = e; // 2
return true;
}
ArrayList insecurity is mainly reflected in two aspects: Adapted from: https: //www.jianshu.com/p/41be1efe5d65
One reason : mark 2 is not an atomic operation, JMM and read only guarantee the basic atomic assignment, the other not guaranteed, this can be split into two steps:
elementData[size] = e;
size++;
Single-threaded implementation of the code no problem, but to a multi-threaded environment may have a problem. One thread may overwrite the value of another thread.
- The list is empty size = 0.
- Executing the thread A
elementData[size] = e;
suspended after. The A "a" in the index for the 0 position. At this size = 0. - B thread of execution
elementData[size] = e;
because the size = 0, so the B "b" in the index for the 0 position, then just the data A to the overwritten. - Thread size the value B 1 is increased.
- A thread size is increased to a value of 2.
This way, when the ideal case after the thread A and thread B are executed should be completed "a" at an index position 0, "b" is marked 1 position. The reality is indeed at index position 0 is "b", the position index 1 is nothing, is null
for two reasons: mark a thread A is executed after execution does not continue ensureCapacity (size + 1) In this case exactly equal minCapacity oldCapacity, B thread executed again, since the same minCapacity equal oldCapacity, ArrayList and not increasing the length, B the thread may continue to execute the assignment (elementData [size] = e) and size ++ also performed at this time , the CPU performs assignment went a thread, since the size value added. 1, the maximum size value is greater than the length of ArrayList, thus giving rise to an array bounds exception. For example: ArrayList default array size is 10. Suppose now been added into the nine elements, size = 9. Perform the following steps:
- A thread executing the add function is
ensureCapacityInternal(size + 1)
to hang. - Thread B started, find the need to check the array capacity expansion. Thus the "b" in the index for the position 9, and the size is incremented by one. At this time, size = 10.
- A thread is then performed, attempts to "a" in the position labeled 10 because size = 10. But because the array has not expansion, the biggest index was 9, it will throw an array bounds exception