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);
后来发现arraylist有自己的序列化逻辑,所以里面的arr通过fastjson的序列化方式打印出来可能不对,
size可能是错的,它里面的elementData是自定义的序列化,其实这段话没有意义,跟序列化无关

第一个值为空可能是因为两线程copy和赋值错序了,第一个线程size++=e了,第二个线程copy将空的赋进去,把第一个置空了,但为什么只有第一个为空?因为之后的至少都有值,不会有把空copy进arraylist里的情况


综上所述,加粗的两段话分别是arraylist在并发情况下size小和第一个为空的原因,也是我瞎猜的,但估计也差不多



总结如下:

1.线程并发问题,首要就分析共享变量

2.object对象的序列化方法很重要

3.写的太粗糙了,以后再改


 
 


猜你喜欢

转载自blog.csdn.net/xiaoe3504xiaoe/article/details/80747659
今日推荐