Recently, looking at the "actual Java Virtual machine" book, see the Lock and concurrent chapters, see the following a multithreaded use of ArrayList example:
Two threads t1 and t2 add data to numberlist at the same time, because ArrayList is thread insecure and therefore causes the added data to be wrong, which I can understand, but it does have the following error:
I can not understand it, ArrayList is not automatic expansion, there is no length limit, why the array of subscript out of bounds such a mistake.
For ease of analysis, I made a little change to the code:
The results of the execution are:
Sometimes there will be null,
With all sorts of puzzled, see ArrayList Add Process:
First of all, ArrayList is based on the array, is a dynamic array, its capacity can automatically grow, similar to the C language Dynamic application memory, dynamic growth of memory.
For ArrayList, it implements the list interface, the underlying use of the array to save all elements. Its operations are essentially an array of operations.
1, the error in the program at Java.util.ArrayList.elementData (arraylist.java:400) and at Java.util.ArrayList.add (arraylist.java:441), They belong to the Add () method.
The source code is as follows:
Add operation, first call ensurecapacityinternal (size + 1) to ensure that the capacity of the array is always sufficient, where size is the number of tuples in the Elementdata array, initially 0.
In the Ensurecapacityinternal () function, if the array has no elements and gives the array a default size, it selects the value that is instantiated and the larger value in the default size, and then calls Ensureexplicitcapacity ().
function body, Modcount is the number of times the array has changed size. If, then, if the array length is less than the default capacity of 10, then the method to enlarge the size of the array is called grow ().
The function grow () explains how an array-based ArrayList is scaled up. When an array is expanded, the elements in the old array are copied back into the new array, and the capacity of the array increases by approximately 1.5 times times its original capacity.
Next go back to the Add () function, continue executing,elementdata[size++] = e; This line of code is the problem, and when you add an element, it can be done in two steps: 1. Place this element in the position of elementdata[size]; 2. Increase the value of Size.
In the case of a single-threaded run, if Size = 0, after adding an element, the element is at position 0 and size=1;
In the case of multithreading, for example, with two threads, thread A first stores the elements in position 0. However, when the CPU dispatches thread A to suspend, thread B gets the chance to run. Thread B also adds elements to this ArrayList because the Size is still equal to 0 (note Well, we assume that adding an element is two steps away, and thread A only completes step 1), so thread B also stores the element at position 0. Both thread A and thread B continue to run, increasing the value of Size. Well, let's take a look at the ArrayList, the element is actually only one, stored in position 0, and Size is equal to 2. This is "thread unsafe". This explains why NULL is present in the collection.
But the array subscripts cannot be explained by this alone. We observed the array subscript at the time of the crossing, 10, 15, 22, 33, 49, and 73 respectively. In combination with the previous array automatic mechanism, the initial length of the array is 10, the first expansion is 15=10+10/2, the second expansion 22=15+15/2, the third expansion 33=22+22/2 ... and so on, it is not difficult to find that the Cross-border anomaly occurs when the array expands.
Which gave me an idea, I suppose, because there is no synchronization of the method, leading to such a phenomenon, with the first exception, that is, the subscript 15 o'clock exception for example. When 14 elements have been added to the collection, a thread takes the lead in the Add () method, and when the ensurecapacityinternal (size + 1) is executed, it is found that an element can be added, so the array is not expanded, but the thread is then blocked here. Then another thread goes into the Add () method, executes ensurecapacityinternal (size + 1), because the previous threads did not add elements, so the size is still 14, still do not need to expand, so the thread began to add elements, so that size++, into 15, The array is full. The thread that just blocked the elementdata[size++] = e; statement begins execution, adds a 16th element to the collection, and the array has only 15 capacity, so there is an array subscript out of bounds exception.