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线程再执行打印,不然输出就不会准确。结果如下:
Java 集合ArrayList 并发add

  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]
版权声明:玥玥 发表于 2021-05-04 11:07:50。
转载请注明:Java 集合ArrayList 并发add | 女黑客导航