源码分析:
arraylist 类:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
/**
* 列表元素集合数组
* 如果新建ArrayList对象时没有指定大小,那么会将EMPTY_ELEMENTDATA赋值给elementData,
* 并在第一次添加元素时,将列表容量设置为DEFAULT_CAPACITY
*/
transient Object[] elementData;
/**
* 列表大小,elementData中存储的元素个数
*/
private int size;
}
add 方法源码:
public boolean add(E e) {
/**
* 添加一个元素时,做了如下两步操作
* 1.判断列表的capacity容量是否足够,是否需要扩容
* 2.真正将元素放在列表的元素数组里面
*/
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
线程不安全分析:
多线程的情况下:
1、 两个线程同时进行容量检查,并发现目前容量是符合插入的,接下来某个线程执行下面的语句,进行元素赋值,并将size 进行 +1操作,如果此时数组的size 正好等于数组的容量,则另一个线程再进行赋值操作时必然会出现 数组下标越界的情况。
2、在容量合适的情况下,一个线程进行类赋值操作,另一个线程在之前线程、进行size + 1操作之前,也进行类赋值操作,会导致之前线程所赋的值被覆盖。验证:
public static void main(String[] args) throws InterruptedException {
final List<Integer> list = new ArrayList<Integer>();
// 线程A将0-1000添加到list
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 1000 ; i++) {
list.add(i);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
// 线程B将1000-2000添加到列表
new Thread(new Runnable() {
public void run() {
for (int i = 1000; i < 2000 ; i++) {
list.add(i);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
Thread.sleep(1000);
// 打印所有结果
for (int i = 0; i < list.size(); i++) {
System.out.println("第" + (i + 1) + "个元素为:" + list.get(i));
}
}
结果:
第7个元素为:3
第8个元素为:1003
第9个元素为:4
第10个元素为:1004
第11个元素为:null
第12个元素为:1005
第13个元素为:6
从结果可以看出,第二种情况出现了。