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线程再执行打印,不然输出就不会准确。结果如下:
- 少元素的原因,可能是同一个数组位置被覆盖了,因为size++并不是原子性的,所以可能线程A自增的时候,A的cpu时间片用完了,然后线程B也进行一次自增,导致A的自增被覆盖了,所以先完成的线程更新的数据会被后完成的线程覆盖掉。
- 出现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]