都说ArrayList是线程不安全的,那为什么不安全呢。根据官方提供的源码,
我是这样理解的,ArrayList的成员方法都不是原子操作的,比如add(E)方法,该方法是在集合的尾部加入一个一个元素.
add(E)源码如下:
/**
* 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;
}
网上的思路大致是这样的:
add(E)操作有两步,1>尾部添加元素 2>修改集合的容量 ,容量加一。
因为是两步,所以必然存在A走完了第一步骤,比如将"张三"放在了index=10的地方,此时还没来得及修改容量,
此时A被挂起,线程B切进来了,因为总容量没变,所以继续在尾部添加,于是index=10的位置被B线程的值占用了,
也就是A添加的值操作就被B冲了,后面的操作继续,A修改容量加1,B修改容量加1。此时容量增加了2,元素只加入了1,所以线程不安全
这样理解没问题,只是根据源码,先执行的是修改容量,然后才是添加元素。这该怎么解释啊?
先修改了容量,哪怕A被暂停了,B执行了,那么结果也是容量增加了2,哪怕后续的操作发生异常,那也没问题啊,因为ArrayList是容许空值得啊!!
求解啊,各位大神
继续研究,更让人痛苦,下面是测试代码:
package sourceCode.ArrayList;
import java.util.ArrayList;
public class arrayListTest implements Runnable {
private ArrayList<Integer> arry = null;
public arrayListTest(ArrayList<Integer> arry) {
this.arry = arry;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
arry.add(1);
}
System.out.println(arry.size());
}
public static void main(String[] args) {
ArrayList<Integer> a = new ArrayList<Integer>();
for (int i = 0; i < 100; i++) {
new Thread(new arrayListTest(a)).start();
}
}
}
解读:这例子也不是我写的。很好理解,100个线程,每个线程向集合中添加10个元素,按理最终集合容量应该是1000,但是运行结果不是的,
所以说,的确是线程不安全的,但是我理解不透啊!!!
求指点,路过的各路大神