说明ArrayList,HashSet,HashMap是线程不安全的以及解决方法

同时有30个线程去同时读写list集合

public static void listNotSafe(){
    //采用Collections工具类将ArrayList变为线程安全的
    List<String> list =new ArrayList<>();// Collections.synchronizedList(new ArrayList<>());//new Vector<>();//new ArrayList<>();

    for (int i = 0; i < 30; i++) {
        new Thread(() -> {
            list.add(UUID.randomUUID().toString().substring(0,8));
            System.out.println(list);//每次运行得到的结果都不相同
        },String.valueOf(i)).start();
        //如果循环次数增加到30次,会报java.util.ConcurrentModificationException异常
        //Vector线程安全
    }
}

由于线程不安全,则会报并发修改异常

> 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.atguigu.juc.NotSafeDemo.lambda$listNotSafe$1(NotSafeDemo.java:68) 
> at java.lang.Thread.run(Thread.java:748) 

1.故障现象

  • java.util.ConcurrentModificationException
  • 2.导致原因
  • 多个线程同时读写list,造成异常
  • 3.解决方案
    3.1 Vector
    3.2 Collections.synchronizedList(new ArrayList<>();
    3.3CopyOnWriteArrayList<>();
public static void listNotSafe(){
    //采用Collections工具类将ArrayList变为线程安全的
    List<String> list =new CopyOnWriteArrayList<>();// Collections.synchronizedList(new ArrayList<>());//new Vector<>();//new ArrayList<>();

    for (int i = 0; i < 30; i++) {
        new Thread(() -> {
            list.add(UUID.randomUUID().toString().substring(0,8));
            System.out.println(list);//每次运行得到的结果都不相同
        },String.valueOf(i)).start();
        //如果循环次数增加到30次,会报java.util.ConcurrentModificationException异常
        //Vector线程安全
    }
}
  • CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,
    
  • 而是先将当前容器Object[]进行Copy,复制出一个新的容器Object[] newElements,然后向新的容器Object[] newElements里添加元素。
  • 添加元素后,再将原容器的引用指向新的容器setArray(newElements)。
  • 这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
  • 所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

CopyOnWriteArrayList<>()的源码

 public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
             Object[] elements = getArray();
             int len = elements.length;
             Object[] newElements = Arrays.copyOf(elements, len + 1);
             newElements[len] = e;
             setArray(newElements);
             return true;
         } finally {
             lock.unlock();
         }
     }

先创建一个lock锁,然后对底层的数组进行get并得到数组长度,然后复制一份长度加一的新数组,并把此事拿到锁的类的元素加入新数组,最后进行set并返回true,最后解锁。这就是CopyOnWriteArrayList的底层线程安全执行步骤

集合同理

public static void setNotSafe(){
    Set<String> set = new CopyOnWriteArraySet<>();//Collections.synchronizedSet();//new HashSet<>();
    new HashSet<>();
    for (int i = 0; i < 30; i++) {
        new Thread(() -> {
            set.add(UUID.randomUUID().toString().substring(0,8));
        },String.valueOf(i)).start();
    }
}

HashMap稍有变化,new的类为ConcurrentHashMap<>();

public static void mapNotSafe(){
    Map<String,String> map =new ConcurrentHashMap<>();//Collections.synchronizedMap();// new HashMap<>();//数组+链表+红黑树D
    //Map<String,String> map2= new HashMap<>(1000);//数组+链表+红黑树D
    for (int i = 0; i < 30; i++) {
        new Thread(() -> {
            map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
            System.out.println(map);
        },String.valueOf(i)).start();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值