ArrayList为什么不是线程安全的

线程安全的问题,只有在涉及到多线程的时候才会出现,在单线程的环境中,是不会出现线程安全的问题。常用的集合类型ArrayList为什么不是线程安全的呢? 就是因为他的add方法,先看源码

// 添加元素方法
    public boolean add(E e) {
          
   
        // 判断是否需要扩容,size+1比当前集合长度大则扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    private void ensureCapacityInternal(int minCapacity) {
          
   
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
          
   
        // 如果elementData为空,即长度为0时,返回10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
          
   
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    
    private void ensureExplicitCapacity(int minCapacity) {
          
   
        modCount++;

        // overflow-conscious code
        // 如果size+1比当前的elementData的长度大,即这次插入数据后就达到最大容量,则需要扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    // 扩容
    private void grow(int minCapacity) {
          
   
        // overflow-conscious code
        int oldCapacity = elementData.length;
        // 扩容后大小为原来的1.5倍,>>1为二进制形式向右移动一位,即除以2
        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指向新数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

那么线程安全的问题出在了哪里呢,有两个原因: 一:数据插入时覆盖 因为elementData[size++] = e;并不是一个原子操作,或者说size++并不是一个原子操作,而是分为两步 elementData[size] = e; size = size+1; 先读取size,再+1,那就会又可能出现资源竞争 现在size为1,A线程与B线程同时进行插入操作,A线程往下标为1的位置插入A,elementData[1] = A;还没等A线程执行size = size + 1;B线程就又执行赋值操作了,此时size还是为1,所以elementData[1] = B;那么最后elementData[1] = B。 二:数据插入时数组越界 具体逻辑如下: 数组长度为默认的10,size为9,此时A线程插入A,B线程插入B,两者都在调用ensureCapacityInternal方法进行容量判断,都不需要进行扩容,A线程插入成功,size = 10,此时B线程再插入,下标为10,但是因为没有扩容,已经超过数组的长度,所以会报出一个数组越界的异常ArrayIndexOutOfBoundsException。

解决方案:使用线程安全的list,如:Vector,SynchronizedList,SynchronizedList。 Vector,SynchronizedList都是全部方法加上同步对象锁,不推荐 SynchronizedList适用于读多写少的场景,读取时不加锁,只有在写入时才会加锁,再进行复制替换操作。

经验分享 程序员 微信小程序 职场和发展