zoukankan      html  css  js  c++  java
  • 为什么ArrayList线程不安全?为什么ArrayList在多线程情况下会报ConcurrentModificationException?

    看下面的第一个例子,并发读写:

    package com.andy.juc;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.UUID;
    
    public class UnsafeList {
        public static void main(String[] args) {
            List<String> list=new ArrayList<>();
    
            System.out.println(list);
            for(int i=0;i<10;i++){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        list.add(UUID.randomUUID().toString());
                        System.out.println(list);
                    }
                },String.valueOf(i)).start();
            }
        }
    }

    上面的代码在System.out.println(list);这一行为报错,报错信息如下。

    Exception in thread "0" Exception in thread "8" java.util.ConcurrentModificationException
        at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
        at java.util.ArrayList$Itr.next(ArrayList.java:851)
        at java.util.AbstractCollection.toString(AbstractCollection.java:461)
        at java.lang.String.valueOf(String.java:2994)
        at java.io.PrintStream.println(PrintStream.java:821)
        at com.andy.juc.UnsafeList$1.run(UnsafeList.java:17)
        at java.lang.Thread.run(Thread.java:745)

    原理如下,迭代器读next元素的时候,会检查modcount和最开始的modcount是不是一样的,相当于一个乐观锁。

    而在ArrayList的add方法里面,每次新增一个元素会把modcount给加1.

    所以modcount修改之后,就会报这个错误。

    需要注意的是,如果只是对ArrayList进行add操作,并不会发生ConcurrentModification报错,这个也是面试经常问的问题。

    import java.util.ArrayList;
    import java.util.List;
    import java.util.UUID;
    
    public class UnsafeList {
        public static void main(String[] args) {
            List<String> list=new ArrayList<>();
    
            System.out.println(list);
            for(int i=0;i<100;i++){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        list.add(UUID.randomUUID().toString());
                    }
                },String.valueOf(i)).start();
            }
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(list);
        }
    }

    扩展只是

    为什么ArrayList底层用了数组,但是可以不用指定数组的大小?

    因为ArrayList需要扩容,扩容的时候用了新的数组。

    下面看一个ArrayList并发写的多线程问题,创建的size不是10000个,而是小于10000个:(多线程的例子一次有可能测不出效果,可以多试几次,或者增加线程个数)

    import java.util.ArrayList;
    import java.util.List;
    import java.util.UUID;
    
    public class UnsafeList {
        public static void main(String[] args) {
            List<String> list=new ArrayList<>();
    
            System.out.println(list);
            for(int i=0;i<10000;i++){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        list.add(UUID.randomUUID().toString().substring(0,6));
                    }
                },String.valueOf(i)).start();
            }
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(list.size());
            System.out.println(list);
        }
    }

    执行结果如下:

  • 相关阅读:
    判断php变量是否定义,是否为空
    HTTP Client 编写
    推荐《冒号课堂——编程范式与OOP思想》
    一些免费的HTML编辑器
    如何判断mysql中数据表中两个列之间的相同记录和不同记录
    PostgreSQL 8.4, SQL Server 2008, MySQL 5.1比较
    JDBC纵览
    使用jdbc连接sql数据库
    关于PHP中变量的判定
    如何判断数据库中是否存在一个数据表
  • 原文地址:https://www.cnblogs.com/mkl34367803/p/14764102.html
Copyright © 2011-2022 走看看