zoukankan      html  css  js  c++  java
  • ConcurrentHashMap的put方法 and initTable实现

     本文主要探讨concurrentHashMap的put方法实现。其中有经典代码(CAS)实现 
    final V putVal(K key, V value, boolean onlyIfAbsent) {
        if (key == null || value == null) throw new NullPointerException();                   //此句代码可看出,该map的key是不能为null
        int hash = spread(key.hashCode());                                                    //获取key的hash值
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {                                                     //可能会存在一次无法添加成功,需多次尝试
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)                                         //初始化map时,里面是不包含任何数组的
                tab = initTable();                                                            //初始化table               
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {                          //获取该key应该存在哪个位置,如果该位置为null,则表示将该key,value放在该位置
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))                          //采用cas命令进行原子操作,若赋值成功,则中断循环,否则就继续循环,直至成功
                    break;                   // no lock when adding to empty bin
            }
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else {
                V oldVal = null;
                synchronized (f) {                                                            //正常情况:加锁方式存node
                    if (tabAt(tab, i) == f) {                                                 //该判断的作用,主要是为了保证多线程操作时,避免数据被别的线程更改,如果不相等,则说明数据被其他线程修改,则重新执行循环,直至数据插入成功
                        if (fh >= 0) {                                                        //key的hash值大于0
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {                       //如果key完全一样,则直接更改value值
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                if ((e = e.next) == null) {                                  //进入循环,找到该链表下最后一个元素,lastNode.next = new Node();
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        else if (f instanceof TreeBin) {
                            Node<K,V> p;
                            binCount = 2;
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }
     
     
    private final Node<K,V>[] initTable() {
        Node<K,V>[] tab; int sc;
        while ((tab = table) == null || tab.length == 0) {                                   //如果tabe为null,则进入初始化tab
            if ((sc = sizeCtl) < 0)                                                          //此处代码很经典,结合下面的elseif看
                Thread.yield(); // lost initialization race; just spin
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {                           //如果sc当前值与期望值不同,cas操作将返回false,说明有其他线程已经完成初始化,造成值不同
                                                                                             //如果sc当前值与期望值相同,则将sc置为-1,这样其他线程在上一步判断时,就会阻塞。因为该步是原子操作,所以会进入该条件下代码块
                try {
                    if ((tab = table) == null || tab.length == 0) {
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;                            //如果sc没有在初始化时设置,就使用默认值16
                        @SuppressWarnings("unchecked")
                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];                      //设置一个长度为16的数组
                        table = tab = nt;
                        sc = n - (n >>> 2);                                                  //将sc设置为12
                    }
                } finally {
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;                                                                          //此处完成initTable操作
    }
    

      

     
     
     
  • 相关阅读:
    [WPF]Win10便签软件
    [WPF]使用Fody提高效率
    [WPF]限制程序单例运行
    [WPF]创建系统栏小图标
    Run Performance Testing Which Was Distributed To Multiple Test Agents
    FxZ,C#开发职位面试测试题(30分钟内必须完成)
    BYS推荐MS前端PhoneCall面试问题整理-2
    BYS推荐MS前端PhoneCall面试问题整理-1
    LxNx前端F2F面试问题整理
    复杂DIV交错布局
  • 原文地址:https://www.cnblogs.com/qinghua0310/p/7216123.html
Copyright © 2011-2022 走看看