小白日更第六十一天->你没见过的ArrayList并发问题

关于ArrayList我们都知道它是集合不安全的集合类。今天闲的无聊,敲了一下,想看一下在多线程的情况下插入数据会不会报并发修改异常的错误。先看代码:

package com.qcby.algorithm;
import java.util.*;

public class TestDemo {
     
    public static void main(String[] args) {
     
	ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i <10 ; i++) {
     
            list.add(i);
        }
        new Thread(()->{
     
                for (int j = 10; j <200 ; j++) {
     
                    list.add(j);
                }
            },Thread.currentThread().getName()).start();

        new Thread(()->{
     
            for (int j = 20; j <300 ; j++) {
     
                list.add(j);
            }
        },Thread.currentThread().getName()).start();

        new Thread(()->{
     
            for (Integer integer : list) {
     
                System.out.println(integer);
            }
        }).start();

        new Thread(()->{
     
            for (int j = 30; j <400 ; j++) {
     
                list.add(j);
            }
        },Thread.currentThread().getName()).start();

        new Thread(()->{
     
            for (int j = 40; j <500 ; j++) {
     
                list.add(j);
            }
        },Thread.currentThread().getName()).start();

运行结果
小白日更第六十一天->你没见过的ArrayList并发问题_第1张图片
看来确实会报这个我们预期的这个并发修改异常,在意料之中,因为在多线程的情况下,可能两个线程都往ArrayList的数组中的同一个插入值,只不过是顺序先后的问题,导致数据覆盖,相当于丢失了。比如我们在ArrayList中插入10000条数据,最后大概率数据会达不到10000条。所以它是集合不安全的类。

那我们再看看它在并发的情况下还会报什么错?

package com.qcby.algorithm;
import java.util.*;

public class TestDemo {
     
    public static void main(String[] args) {
     
    ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i <10 ; i++) {
     
            list.add(i);
        }
        new Thread(()->{
     
                for (int j = 10; j <200 ; j++) {
     
                    list.add(j);
                }
            },Thread.currentThread().getName()).start();

        new Thread(()->{
     
            for (int j = 20; j <300 ; j++) {
     
                list.add(j);
            }
        },Thread.currentThread().getName()).start();

        new Thread(()->{
     
            for (int j = 30; j <400 ; j++) {
     
                list.add(j);
            }
        },Thread.currentThread().getName()).start();

        new Thread(()->{
     
            for (int j = 40; j <500 ; j++) {
     
                list.add(j);
            }
        },Thread.currentThread().getName()).start();
        new Thread(()->{
     
            try {
     
                Thread.sleep(2000);
                for (Integer integer : list) {
     
                    System.out.println(integer);
                }
                System.out.println("-------------");
                System.out.println(list.size());
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
        }).start();
    }
}

小伙伴们可以想一想运行结果会报什么错?
小白日更第六十一天->你没见过的ArrayList并发问题_第2张图片
看下这个错误,数组下标越界。那为什么会报这个错呢?
我们知道Arraylist插入数据的时候会进行数组扩容,我们四条线程同时插入数据,在并发的情况,可能会发生这样一种情况,就是两个线程都往数组的最后一个位置插入数据,一个线程插入完以后,数组中没有位置了,需要进行扩容,但是扩容的操作还没有完成呢,线程就开始插入,那肯定会报数组下标越界了。而且在扩容没完成之前,线程插入的数据都会丢失。我刚才运行了好几次才报了一次数组下标越界异常,也就是说可能会发生数组下标越界的错误,但并不一定会发生。
小白日更第六十一天->你没见过的ArrayList并发问题_第3张图片
奇怪的事情发生了,我运行了不下10次,每次都输出Arraylist的size,为什么每次长度都是1310呢?
小白日更第六十一天->你没见过的ArrayList并发问题_第4张图片
我和小伙伴讨论了一下,可能是每次四个线程跑完,数组扩容完之后,size都是1310,但是里面很有可能没有放满数据,现在我们把最后一个线程的for循环条件由500改为1000,我们再来看一下结果如何
小白日更第六十一天->你没见过的ArrayList并发问题_第5张图片

改为1000后,size大小多了500,也就是1810,反复运行结果都是1810,也就验证了我和小伙伴的想法是对的。
然后我又把最后一个for循环的条件改为1400,我们再来看看运行结果:
小白日更第六十一天->你没见过的ArrayList并发问题_第6张图片
小白日更第六十一天->你没见过的ArrayList并发问题_第7张图片

我代码运行了不下20遍,几乎都是2210这个数字。我举得并发情况下不确定因素很多,和我们电脑的状态也有关系,但是我比较好奇的是为什么换成1400后,或者其他别的数字,运行几次后size的大小几乎就固定不变了呢?

有知道的小伙伴希望在评论区不吝赐教~

你可能感兴趣的:(JUC,java,多线程,并发编程)