• 集合类不安全之Set


    1、不安全的Set

    上代码:

        public static void main(String[] args) throws Exception {
    
            Set<String> set = new HashSet<>();
    
            for (int i = 0;i<30;i++){
                new Thread(()->{
                    set.add(Thread.currentThread().getName());
                    System.out.println(Thread.currentThread().getName()+"	"+set);
                }).start();
            }
        }
    
    //运行结果如下:多线程共同修改set集合,造成了并发修改异常
    java.util.ConcurrentModificationException
    	at java.util.HashMap$HashIterator.nextNode(HashMap.java:1445)
    	at java.util.HashMap$KeyIterator.next(HashMap.java:1469)
    	at java.util.AbstractCollection.toString(AbstractCollection.java:461)
    	at java.lang.String.valueOf(String.java:2994)
    	at java.lang.StringBuilder.append(StringBuilder.java:131)
    	at juc.ContainerNotSafeDemo.lambda$main$0(ContainerNotSafeDemo.java:17)
    	at java.lang.Thread.run(Thread.java:748)
    

    2、安全的解决方式

    使用CopyOnWriteArraySet解决

        public static void main(String[] args) throws Exception {
    
            Set<String> set = new CopyOnWriteArraySet<>();//new HashSet<>();
    
            for (int i = 0;i<30;i++){
                new Thread(()->{
                    set.add(Thread.currentThread().getName());
                    System.out.println(Thread.currentThread().getName()+"	"+set);
                }).start();
            }
        }
    

    关于写时复制技术在这篇博客里写过,不再赘述。深入探索一下,看源码:

    public class CopyOnWriteArraySet<E> extends AbstractSet<E>   
            implements java.io.Serializable {
        private static final long serialVersionUID = 5457747651344034263L;
    
        private final CopyOnWriteArrayList<E> al;
    
        /**
         * Creates an empty set.
         */
        public CopyOnWriteArraySet() {            //无参构造方法
            al = new CopyOnWriteArrayList<E>();  //实际上创建的是CopyOnWriteArrayList
        }
        ....
    }
    

    3、关于HashSet的补充

    HashSet底层是什么?看源码:

        /**
         * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
         * default initial capacity (16) and load factor (0.75).
         */
        public HashSet() {
            map = new HashMap<>();
        }
    

    注释的意思是:创建一个空的HashMap,初始容量是16,负载因子是0.75.

    HashSet在add的时候传的是一个值并不是<k,v>,比如下边:

    new HashSet<>().add("hello");
    

    确定HashSet底层是HashMap吗???

    这是一个容易被忽略的地方,查看HashSet的add方法源码:

        /**
         * Adds the specified element to this set if it is not already present.
         * 如果指定的元素尚未出现,则将其添加到此集合。
         * More formally, adds the specified element e to this set if
         * this set contains no element e2 such that
         * (e==null?e2==null:e.equals(e2)).
         * If this set already contains the element, the call leaves the set
         * unchanged and returns false
         * 如果该集合已经包含元素,则调用将不修改集合,返回false。
         * @param e element to be added to this set
         * @return <tt>true</tt> if this set did not already contain the specified
         * element
         */
        public boolean add(E e) {
            return map.put(e, PRESENT)==null;
        }
    

    可以看到,add方法其实是给HashMap添加了Key,value是PRESENT,它是一个Object类型的常量。调用的还是map的add方法。



  • 相关阅读:
    吴恩达读书笔记【5】-流水线与端到端
    标准与扩展ACL 、 命名ACL
    VLAN间通讯 、 动态路由RIP
    HSRP热备份路由协议 、 STP生成树协议
    VLAN广播域划分
    应用层
    包格式及IP地址 、 网络层协议及设备
    传输层 、 应用层
    数据链路层解析 、 交换机基本配置
    网络基础3
  • 原文地址:https://www.cnblogs.com/simon-1024/p/12096198.html
Copyright © 2020-2023  润新知