【ArrayList的实现】
ArrayList的实现主要有: 一个Object的elementData的数组保存所有的元素;一个size变量保存当前数组中已经添加了多少元素。
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
【add操作的源码】
add操作大概分为两步:一、判断列表的capacity容量是否足够,是否需要扩容;二、将元素放在列表的元素数组里
线程不安全的隐患(1):
假设列表当前大小为8,即size=8
线程A开始进入add方法,这是它获得到size的值为8,调用ensureCapacityInternal方法进行容量判断。
线程B此时也进入add方法,它获取到的size值也为8,也开始调用ensureCapacityInternal方法。
线程A发现需求大小为8+1=9,而elementData的大小刚好为9,可以容纳。于是它不再扩容,返回。
线程B也发现需求大小为9,也可以容纳,返回。
线程A开始进行设置值操作,执行elementData[size++]=e后,size变为9。
线程B也开始进行设置值操作,执行elementData[size++]即elementData[9]=e。此时会报一个数组越界的异常ArrayIndexOutOfBoundsException。
线程不安全的隐患(2):
另外elementData[size++]=e设置值的操作也会导致线程不安全。因为这步操作不是一个原子操作,它由以下两步构成:
1、elementData[size]=e;
2、size = size+1;
假设elementData当前size大小为0。
线程A添加一个元素“A”,将“A”放在elementData下标为0的位置上。
接着线程B刚好也要添加一个元素"B",且走到了第一步操作。此时线程B获取的size值依然是0,于是线程B将“B”也放在了elementData下标为0的位置上。
然后,线程A将size的值增加为1;线程B将size的值增加为2。
这样线程A、B执行完成后,elementData的期待结果为:size为2,elementData[0]="A",elementData[1]="B"。
而实际情况是:size为2,elementData[0]="B",elementData[1]=null。
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}