一直在说ArrayList是非线程安全的,到底是为什么呢?
以ArrayList的add()方法为例,添加操作并不是一步完成,而是分为两步:
1.先在elementData[index]位置上添加一个新的元素
2.接着在为size进行+1操作。
描述:
如果此添加过程是在多线程的环境下,例如线程1已经完成了第一步将元素添加进了elementData[]中,但是此时的size并没有进行+1操作,而现在来了一个线程2,线程1的调度就结束了,size并没有+1,而线程2继续为elementData[]添加值,此时的size状态依然为之前的状态,接着由于线程1需要完成add()全过程,所以就发生了两次size++,此时添加的元素位置索引与size值就不匹配了。
所以这就是非线程安全。
一、ArrayList的继承结构
在解读源码之前我们需要对ArrayList的内部继承结构做了解:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
AbstractList:实现List接口操作规范,增、删、遍历等操作。
RandomAccess:提供随机访问功能
Cloneable:提供可拷贝功能
Serializable:提供可序列化功能
本文不对ArrayList成员变量及构造进行一一解析,ArrayList源码详细解析放在我的github上:
https://github.com/NolanJcn/Java-Is-Simple/tree/master/Source%20Code%20Analysis
欢迎star,后续将会陆续更新其他容器类源码解析!
想将ArrayList烂透与心中,以下几点必须掌握:
(1)扩容机制
(2)浅拷贝
(3)快速报错机制
(4)批量删除算法
(5)遍历方式
在我认为只要明白了以上几点,一千行ArrayList源码的阅读将能顺畅无比。
1.扩容机制
ArrayList源码中能需要扩容的无非就如下几类方法:add()、addAll()、readObject()。
扩容要从一个方法 ensureExplicitCapacity 讲起。
例如从 add(E e) 方法讲起:
//将指定的元素追加到此列表的末尾
public boolean add(E e) {
//扩容
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
方法很简单,先扩容,再维护 size 变量 ,接着往里填元素。
而添加方法中的扩容方法如下:
//中间过渡方法:确保要扩容的量为多少
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
此时可以发现方法中又调用了其他方法:继续跟踪
//确定了要扩容的量
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//如果需要扩容的量大于数组的长度