ArrayList为什么线程不安全?怎么进行扩容?

先来看一个线程不安全的样例:

public class TestArrayList {
    private static List<Integer> list = new ArrayList<>();
    private static CountDownLatch c = new CountDownLatch(100);

    public static void main(String[] args) {
        for (int i = 1; i <= 1000; i++) {
            final int a = i;
            new Thread(() -> {
                //模拟在一个自增操作前可能还要处理其他业务
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                list.add(a);
                c.countDown();
            }, "A").start();
        }
        try {
            c.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }

}

打印出来的结果:93(这个每次打出来都可能不同)

再来看下它的add方法,看看为什么线程不安全:

    /**
     *其实看了这个代码,就应该知道为啥不安全了,就是这个size++操作,在多线程的情况下,这个操作, 
     *可能会出现值覆盖,什么意思呢?举个例子,比如size初始值为0,这时候两个线程执行size++操作, 
     *在内存中这两个线程首先会从主内存中拿到这个初始值0,然后在自己的工作内存中完成+1操作,当需 
     *要把这个+1后的值刷新回主内存的时候,A线程被挂起,B线程刷新回到主内存那么主内存的值为1,因 
     *为这个执行的过程非常快,A线程紧着也把1刷新回主内存,这时候A线程的1就覆盖了B线程的1(本来A 
     *线程应该是从主内存中再拿回1加了之后,再向主内存刷新回2),这样就导致了 
     *elemenData[size++]再同一个位置的值可能被下一个线程的e给覆盖了,从而导致这个集合的长度本 
     *应该是100,但是由于线程不安全,长度发生了改变
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    //顺带看下add方法的实现,这个方法就是判断要不要扩容
    private void ensureCapacityInternal(int minCapacity) {
       //如果是new出来的不带构造参数的ArrayList,首先会进这个方法,给第一次准备创建的
       //Object数据给一个10大小的默认值
       if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
          minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
    //这里具体的扩容规则,
    private void ensureExplicitCapacity(int minCapacity) {
       modCount++;

       //第一次add的时候,进行扩容,长度为10;当第十一个元素进来的时候再扩容...
       if (minCapacity - elementData.length > 0)
          grow(minCapacity);
    }
    //具体怎么扩容
    private void grow(int minCapacity) {
        // overflow-conscious code
        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:
        //要扩容的数据,和扩容后的长度,new Obect[10],然后把原数组的值复制进扩容后的数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值