ArrayList并发问题分析,arraylist并发分析
ArrayList并发问题分析,arraylist并发分析
并发问题老是感觉很棘手,这次碰到了一个ArrayList在线程池中add出现null数据的问题,虽然之前就知道ArrayList是非线程安全的,但是具体为啥不安全,为啥会出现空值,没有深入去理解,这次出现这个问题,经过自己分析,基本知道了这类问题出错会出在哪儿,对于这类问题的分析有点谱了
1.问题描述:
for循环线程池中启10个任务进行list.add(),加完后,发现第一个值为空,而且list的size也不是10
2.分析:
线程问题比较不好分析,主要是太抽象,都隐藏在后台,不能直观的观察到,而且时有时无,所以比较难分析
1.第一步,出这种问题,首先确定,肯定是共享变量,非原子化操作引起的
ArrayList的add方法很简单,就下面这两句
ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e;
第一句是扩容,可以排除(其实也有关系),直觉上应该是size++这个操作不是原子性的引起的
2.第二步,给自己插上想象的鸡翅膀
2.1 size小的原因,我猜如下:
最后的几个add,size++都在前一个线程没有加完的基础上++,导致最后的几个对象都加到最后一个位置上
(有待推敲,因为有扩容因素)
2.2 第一个为空的原因,我猜如下:
最开始的为空,第一个线程准备size++,第二个线程已经size++好了,导致两个线程都加到了第二个位置上,
第一个没人设置,所以为空(也不太确定,都走到size++,放置的时候size已经+2了,但为什么中间没有null的出现?)
好,分析完毕
可能是上面的原因,但是很多细节经不起推敲
比如这一步,如果是空的话,就会将size设置为默认值10,但是实际有size为9,或者8的情况
ensureCapacityInternal(size + 1); // Increments modCount!!
说明了什么?这一步没有执行,直接到1,然后用它的算法size=size+size>>1进行扩容,
其实不是没有执行是他自己有自定义的序列化方式,自己的size跟这个elementData的length无关
为什么没执行?就算再怎么乱序,也至少会有一个线程是从size=0开始执行的,只要是0就会默认将Arraylist copy进一个size为10的新list里
private void ensureCapacityInternal(int minCapacity) { if (elementData == EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//其中default_capacity为10,第一次执行的时候minCapacity为1,所以minCapacity为10 } ensureExplicitCapacity(minCapacity); }
ensureExplicitCapacity这里执行的是下面的代码:如果新长度大于arr的长度,就grow他
if (minCapacity - elementData.length > 0) grow(minCapacity);grow代码如下:拿到老size,将新size和老size的3/2进行比较,取大的将arr copy进去
int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity);
size可能是错的,它里面的elementData是自定义的序列化,其实这段话没有意义,跟序列化无关
第一个值为空可能是因为两线程copy和赋值错序了,第一个线程size++=e了,第二个线程copy将空的赋进去,把第一个置空了,但为什么只有第一个为空?因为之后的至少都有值,不会有把空copy进arraylist里的情况
综上所述,加粗的两段话分别是arraylist在并发情况下size小和第一个为空的原因,也是我瞎猜的,但估计也差不多
总结如下:
1.线程并发问题,首要就分析共享变量
2.object对象的序列化方法很重要
3.写的太粗糙了,以后再改
相关文章
- 暂无相关文章
用户点评