ArrayList为什么是线程不安全的/多线程修改会出现的问题

ArrayList基本属性

    // 当创建ArrayList时不指定初始容量,第一次添加数组时将elementData扩容为默认的10
    private static final int DEFAULT_CAPACITY = 10;
    //默认空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    // 存放数据的数组
    transient Object[] elementData;
    //elementData数组中元素个数
    private int size;
    //数组发生size更改的次数,用于标识在迭代过程中不能修改集合,在迭代开始生成Iterator时会赋值int expectedModCount = modCount;每次next()迭代元素时就会检查 expectedModCount 和modCount是否相等,不等则抛ConcurrentModificationException异常,
    //比如迭代中remove(),remove时modCount+1,下次next时检测到不相等则抛异常了
    protected transient int modCount = 0;
    
    //当创建ArrayList时不指定初始容量,使用默认的空数组
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
  

添加逻辑(下面都以创建ArrayList时不指定初始容量,且第一次添加新元素为例说明)

   //添加元素
    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
    
    private void add(E e, Object[] elementData, int s) {
        //1.若size==数组个数,出现这种相等有两种情况
           //1.1 数组不为空且此时数组满了
           //1.2 当创建ArrayList时不指定初始容量时,首次添加元素时size:0=数组个数:0
        if (s == elementData.length)
            //2.扩容
            elementData = grow();
        //3.数组默认添加新元素    
        elementData[s] = e;
        //4.size+1
        size = s + 1;
    }

扩容逻辑

    //扩容后数组大小=旧数组大小+(旧数组大小)>> 1
    private Object[] grow() {
        return grow(size + 1);
    }
    //扩容数组
    private Object[] grow(int minCapacity) {
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }
    //返回扩容后新数组大小,扩容后大小=原来大小的1.5倍左右,若旧数组为空,返回默认大小10
    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        //1.数组旧大小为0
        int oldCapacity = elementData.length;
        //2.数组新大小为=旧大小+旧大小带符号右移1位,即新大小=旧大小+旧大小一半,即扩容原来的一半取整
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity <= 0) {
        //0 = 0 + 0>>1
        //3.新大小=旧大小,出现这种情况只有:创建ArrayList时不指定初始容量时,且第一次添加新元素,返回默认数组大小10
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        
        //4.MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8,若扩容后大小 <= Integer最大值,返回扩容后大小
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }
    
    //当源数组大小+1 大于 Integer.MAX_VALUE - 8,扩容后大小变为Integer.MAX_VALUE,
    //否则扩容后大小变为Integer.MAX_VALUE - 8
    //https://blog.csdn.net/weixin_42462804/article/details/108726206
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE)
            ? Integer.MAX_VALUE
            : MAX_ARRAY_SIZE;
    }

扩容后旧值复制到新数组中

# Arrays.copyOf

    public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }
    
    @HotSpotIntrinsicCandidate
    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
   
# System.arraycopy

    //调用native方法实现真正的复制,src:源数组,srcPos:从哪个索引开始复制,dest:扩容后的空数组,destPos:从dest的哪个索引开始设值,length:复制元素个数
    @HotSpotIntrinsicCandidate
    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

在jdk9之前,添加代码是这样的:

    public boolean add(E e) {
    	//1.扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //2.设置元素(直接用的size变量,此时size可能被其它线程改变,而8之后读取的add方法之前的size放到s中,在add时s不会变其它线程改变)
        elementData[size++] = e;
        return true;
    }

问题一:元素丢失

在这里插入图片描述

问题二:数组存在null元素

在这里插入图片描述

问题三:数组下标越界ArrayIndexOutOfBoundsException

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值