Java 集合ArrayList 并发add

Java 8中多线程对ArrayList 进行添加元素的时候,有概率某个位置会出现null值,也可能缺少元素。我觉得应该是扩容那块出现问题。

public class ThreadTestArrayList {
     
    public static void main(String[] args) throws InterruptedException {
     
        CountDownLatch latch = new CountDownLatch(500);
        LatchDemo ld = new LatchDemo(latch);
        for (int i = 0; i < 500; i++) {
     
            new Thread(ld).start();
        }
        latch.await();
        ld.prif();
    }
}

class LatchDemo implements Runnable {
     

    public CountDownLatch latch;
    public List<Integer> list = new ArrayList<>();

    public LatchDemo(CountDownLatch latch) {
     
        this.latch = latch;
    }

    @Override
    public void run() {
     
      //  synchronized (this){
     
        try {
     
            list.add(0);
        } finally {
     
            latch.countDown();
        }
        //}
    }

    public void prif() {
     
        System.out.println(list);
        System.out.println(list.size());
    }
}

CountDownLatch 这个是JUC里面的闭锁,这里的意思就是让main线程等待,让其他线程先执行add完毕后,main线程再执行打印,不然输出就不会准确。结果如下:
在这里插入图片描述

  1. 少元素的原因,可能是同一个数组位置被覆盖了,因为size++并不是原子性的,所以可能线程A自增的时候,A的cpu时间片用完了,然后线程B也进行一次自增,导致A的自增被覆盖了,所以先完成的线程更新的数据会被后完成的线程覆盖掉。
  2. 出现null的原因,Java8中,直接new ArrayList<>()实例化的时候,底层的数组是空的,大小为0,也就是第一次add会进行扩容的。假设现在有线程A和B分别要插入元素1和2,线程A调用add方法时,会进行它自己的扩容,在自己的工作空间创建新数组,这时A扩容未完成呢,此时线程B调用add方法时发现也需要,这时B也会进行扩容也创建自己的数组==(重点:这里都是在自己空间创建数组)==,假设此时线程A比线程B扩容先完成,此时list的elementDate是新的数组(A构建的),接着执行赋值操作elementDate[size++] = 1,在此之前线程B扩容 拿到的数组仍然是旧的elementDate,于是线程B构造一个新的数组(数据全部为null),然后使list的elementDate指向线程B构造的数组,那么线程A之前的elementDate也就被丢掉了,但是由于size已经自增,所以线程B会在下一个位置赋予2,那么此时数组元素就成了[null,2,x,x,x,x,x,x,x]

你可能感兴趣的:(Java,java,多线程,arraylist)