ArrayList是非线程安全的,但是为什么是非线程安全的呢?

自己通过学习ArrayList的源码,谈一下自己的感悟,不足之处,望多加指出,运维出身自学Java。

ArrayList源码中的属性
//默认容量
private static final int DEFAULT_CAPACITY = 10;
//空数组,如果传入的容量为0时使用
private static final Object[] EMPTY_ELEMENTDATA = {};
//空数组,传入容量时使用,添加第一个元素的时候会重新初始为默认容量大小
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存储元素的数组
transient Object[] elementData;
//集合中元素的个数
private int size;

add操作的源码

public boolean add(E e) {
 		//检查是否需要扩容
        ensureCapacityInternal(size + 1); 
        //把元素插到最后一位
        elementData[size++] = e;
        return true;
    }
private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果是空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA,就初始化为默认大小10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
  }
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // 扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
 }
private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        //新容量为旧容量的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果新容量发现比需要的容量还小,则以需要容量为准
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //如果新容量已经超过最大容量了,则使用最大容量
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //以新容量拷贝出来一个新数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

add操作大概分为两步:
一、判断列表的capacity容量是否足够,是否需要扩容;
二、将元素放在列表的元素数组里

第一个线程不安全的地方

假设当前集合中的元素个数为9,即size=9

线程A开始调用add方法,这是它获得到size的值为9,调用ensureCapacityInternal方法进行容量判断。

同是线程B也开始调用add方法,它获取到的size值也为9,也开始调用ensureCapacityInternal方法。

假设线程A需求为添加一个元素,size大小为9+1=10,而elementData的大小默认为10,可以容纳。于是它不再扩容,返回。

假设线程B需求也是添加一个元素,size大小为9+1=10,也可以容纳,返回。

线程A开始进行设置值操作,执行elementData[size++]=e后,size变10。

线程B同是也开始进行设置值操作,此时size的大小为10,但是线程B已经判断过了elementData大小可以容纳,不需要扩容,执行elementData[size++],即elementData[10++]=e。此时会报一个数组越界的异常ArrayIndexOutOfBoundsException。

第二个线程不安全的地方

另外elementData[size++]=e设置值的操作也会导致线程不安全。因为这步操作不是一个原子操作,它由以下两步构成:

1、elementData[size]=e;

2、size = size+1;

假设elementData当前size大小为0。

线程A添加一个元素“A”,将“A”放在elementData下标为0的位置上。

接着线程B刚好也要添加一个元素"B",且走到了第一步操作。此时线程B获取的size值依然是0,于是线程B将“B”也放在了elementData下标为0的位置上。

然后,线程A将size的值增加为1;线程B将size的值增加为2。

这样线程A、B执行完成后,elementData的期待结果为:size为2,elementData[0]=“A”,elementData[1]=“B”。

而实际情况是:size为2,elementData[0]=“B”,elementData[1]=null。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值