zoukankan      html  css  js  c++  java
  • Java高并发,ArrayList、HashSet、HashMap不安全的集合类

    首先是我们的ArrayList:

    这次我们讲解的是集合的不安全,首先我们都知道ArrayList吧!

    List<String> list=new ArrayList<>();

    首先我们拓展几个知识点,ArrayList底层是Object类型的数组,jdk7之前初始容量是10。

    jdk8之后是空引用,到add之后会变成10,类似于懒加载的机制,其扩容的方式是每次扩容为之前的一半,比如10会扩容成15,15扩容成22,扩容用到的方法时Arrays的copyof方法,OK,接下来后进入主题。

    我们通过Lambda表达式来生成几个线程

     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();
    }

    之后会报异常:
    java.util.ConcurrentModificationException 并发修改异常
    导致原因:多线程并发争抢统一资源(这里指list实例),且没加锁
    那么我们有什么解决办法呢??

    方法一:Vector

    List<String> list = new Vector<>();

    Vector集合,底层给add方法加了synchronized(重锁),同一时间段只有一个个线程,效率低,所以这就是为什么Vector集合安全效率低的原因

    方法二:Collections.synchronizedList

    Collections.synchronizedList(new ArrayList<>());

    这是Connections的静态方法,传入一个ArrayList。

    方法三:CopyOnWriteArrayList

    List<String> list = new CopyOnWriteArrayList<>();

    jdk1.5之后,这个JUC包当中的CopyOnWriteArrayList类,实现了List接口。

    public class CopyOnWriteArrayList<E>
    extends Object
    implements List<E>, RandomAccess, Cloneable, Serializable

    CopyOnWriteArrayList的底层:volatile Object[] array

    其add()方法里面加了锁ReentrantLock:写前复制,length+1

    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();
            }
        }

    注意:CopyOnWriteArrayList的扩容同样用的是Arrays工具类的copyof()方法,但一次扩容1

    接着我们来看Set集合:

    同样扩展点知识点,HashSet底层数据结构是HashMap,HashSet源码构造器是new HashMap(),

    map集合添加元素,丢进去的是K-V键值对

    但是set是无序的集合,set的元素添加是对一个对象进去。数量对不上啊,这不是互相矛盾么?所以接下来分析HashSet的底层实现源码:

    HashSet的add方法实际上调的是map的put方法:,set添加的元素就是put()里面的key,因为set不会重复,keys也就不会重复

    PRESENT实际上就是一个object常量,value并不重要

    同样的步骤:

     Set<String> set=new HashSet<>();
         for(int i=0;i<30;i++){
           new Thread(()->{
            set.add(UUID.randomUUID().toString().substring(0, 8));
            System.out.println(set);
          },String.valueOf(i)).start();
    }

    同样是报错:
    java.util.ConcurrentModificationException
    解决方案:

    方法一:Collections.synchronizedSet

    Set<String> set=Collections.synchronizedSet(new HashSet<>());

    方法二:CopyOnWriteArraySet

     Set<String> set=new CopyOnWriteArraySet();

    也是JUC的里的类,其构造器是new CopyOnWriteArraySet();

    public class CopyOnWriteArraySet<E>
    extends AbstractSet<E>
    implements Serializable

    然后就是我们的Map:

    扩展知识:HashMap(无序无重复)底层是数组+链表(单向)+红黑树。

    HashMap存的是node节点,node里面存K-V键值对,node里面存的是Key-Value,HashMap初始容量为16,负载因子为0.75(到16*0.75的时候扩容)。每次扩容为原值得一倍

    初始容量和负载因子可以通过构造器来改。

    Constructor and Description 
    HashMap() 
    构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。  
    HashMap(int initialCapacity) 
    构造一个空的 HashMap具有指定的初始容量和默认负载因子(0.75)。  
    HashMap(int initialCapacity, float loadFactor) 
    构造一个空的 HashMap具有指定的初始容量和负载因子。  
    HashMap(Map<? extends K,? extends V> m) 
    构造一个新的 HashMap与指定的相同的映射 Map 。  

     同样的操作:

     Map<String, String> map = new HashMap<>();
            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();
            }

    同样的会报错:
    java.util.ConcurrentModificationException
    解决办法:

     Map<String, String> map = new ConcurrentHashMap();

    Java.util.concurrent包下的ConcurrentHashMap:

    public class ConcurrentHashMap<K,V>
    extends AbstractMap<K,V>
    implements ConcurrentMap<K,V>, Serializable

    这次就是本次讲解的JUC之集合不安全,希望能帮助到大家,如果有更好的建议,欢迎大家留言!

  • 相关阅读:
    在vue项目中使用codemirror插件实现代码编辑器功能(代码高亮显示及自动提示)
    解决request.getSession().getServletContext().getRealPath("/")为null问题
    redis数据类型为key的常用命令
    spring boot知识清单
    java 百度地图判断两点距离2
    java 百度地图判断两点距离1
    百度地图js判断点是否在圆形区域内
    百度地图java 判断当前位置是否在多边形区域内
    redis学习之路
    burpsuite插件使用
  • 原文地址:https://www.cnblogs.com/yanl55555/p/13541997.html
Copyright © 2011-2022 走看看