文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java源码解析之ConcurrentHashMap

2024-04-02 19:55

关注

早期 ConcurrentHashMap,其实现是基于:

在进行并发操作的时候,只需要锁定相应段,这样就有效避免了类似 Hashtable 整体同步的问题,大大提高了性能。

Put操作

通过二次哈希避免哈希冲突,然后以 Unsafe 调用方式,直接获取相应的 Segment,然后进行线程安全的 put 操作


public V put(K key, V value) {
 
        Segment<K,V> s;
 
        if (value == null)
 
            throw new NullPointerException();
 
        // 二次哈希,以保证数据的分散性,避免哈希冲突
 
        int hash = hash(key.hashCode());
 
        int j = (hash >>> segmentShift) & segmentMask;
 
        if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
 
             (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
 
            s = ensureSegment(j);
 
        return s.put(key, hash, value, false);
 
    }

其核心逻辑实现在下面的内部方法中:


final V put(K key, int hash, V value, boolean onlyIfAbsent) {
 
            // scanAndLockForPut 会去查找是否有 key 相同 Node
 
            // 无论如何,确保获取锁
 
            HashEntry<K,V> node = tryLock() ? null :
 
                scanAndLockForPut(key, hash, value);
 
            V oldValue;
 
            try {
 
                HashEntry<K,V>[] tab = table;
 
                int index = (tab.length - 1) & hash;
 
                HashEntry<K,V> first = entryAt(tab, index);
 
                for (HashEntry<K,V> e = first;;) {
 
                    if (e != null) {
 
                        K k;
 
                        // 更新已有 value...
 
                    }
 
                    else {
 
                        // 放置 HashEntry 到特定位置,如果超过阈值,进行 rehash
 
                        // ...
 
                    }
 
                }
 
            } finally {
 
                unlock();
 
            }
 
            return oldValue;
 
        }

在写的时候:

机制在Java 8 上的变化:

看看在java8上的put操作


final V putVal(K key, V value, boolean onlyIfAbsent) { if (key == null || value == null) throw new NullPointerException();
 
    int hash = spread(key.hashCode());
 
    int binCount = 0;
 
    for (Node<K,V>[] tab = table;;) {
 
        Node<K,V> f; int n, i, fh; K fk; V fv;
 
        if (tab == null || (n = tab.length) == 0)
 
            tab = initTable();
 
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
 
            // 利用 CAS 去进行无锁线程安全操作,如果 bin 是空的
 
            if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
 
                break;
 
        }
 
        else if ((fh = f.hash) == MOVED)
 
            tab = helpTransfer(tab, f);
 
        else if (onlyIfAbsent // 不加锁,进行检查
 
                 && fh == hash
 
                 && ((fk = f.key) == key || (fk != null && key.equals(fk)))
 
                 && (fv = f.val) != null)
 
            return fv;
 
        else {
 
            V oldVal = null;
 
            synchronized (f) {
 
                   // 细粒度的同步修改操作...
 
                }
 
            }
 
            // Bin 超过阈值,进行树化
 
            if (binCount != 0) {
 
                if (binCount >= TREEIFY_THRESHOLD)
 
                    treeifyBin(tab, i);
 
                if (oldVal != null)
 
                    return oldVal;
 
                break;
 
            }
 
        }
 
    }
 
    addCount(1L, binCount);
 
    return null;
 
}

初始化操作实现在 initTable 里面,这是一个典型的 CAS 使用场景,利用 volatile 的 sizeCtl 作为互斥手段:如果发现竞争性的初始化,就 spin 在那里,等待条件恢复;否则利用 CAS 设置排他标志。如果成功则进行初始化;否则重试。


private final Node<K,V>[] initTable() {
 
    Node<K,V>[] tab; int sc;
 
    while ((tab = table) == null || tab.length == 0) {
 
        // 如果发现冲突,进行 spin 等待
 
        if ((sc = sizeCtl) < 0)
 
            Thread.yield();
 
        // CAS 成功返回 true,则进入真正的初始化逻辑
 
        else if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
 
            try {
 
                if ((tab = table) == null || tab.length == 0) {
 
                    int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
 
                    @SuppressWarnings("unchecked")
 
                    Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
 
                    table = tab = nt;
 
                    sc = n - (n >>> 2);
 
                }
 
            } finally {
 
                sizeCtl = sc;
 
            }
 
            break;
 
        }                                                          
 
    }
 
    return tab;
 
}

当 bin 为空时,同样是没有必要锁定,也是以 CAS 操作去放置。

到此这篇关于Java源码解析之ConcurrentHashMap的文章就介绍到这了,更多相关Java ConcurrentHashMap内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯