广告
返回顶部
首页 > 资讯 > 精选 >Java源码解析之ConcurrentHashMap的示例分析
  • 743
分享到

Java源码解析之ConcurrentHashMap的示例分析

2023-06-15 06:06:29 743人浏览 安东尼
摘要

小编给大家分享一下Java源码解析之ConcurrentHashMap的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!早期 ConcurrentHashMap,其实现是基于:分离锁,也就是将内部进行分段(Segme

小编给大家分享一下Java源码解析之ConcurrentHashMap的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!

早期 ConcurrentHashMap,其实现是基于:

  • 分离,也就是将内部进行分段(Segment),里面则是 HashEntry 的数组,和 HashMap 类似,哈希相同的条目也是以链表形式存放。

  • HashEntry 内部使用 volatile 的 value 字段来保证可见性,也利用了不可变对象的机制以改进利用 Unsafe 提供的底层能力,比如 volatile access,去直接完成部分操作,以最优化性能,毕竟 Unsafe 中的很多操作都是 JVM intrinsic 优化过的。

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

Java源码解析之ConcurrentHashMap的示例分析

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

在写的时候:

  • ConcurrentHashMap 会获取再入锁,以保证数据一致性,Segment 本身就是基于 ReentrantLock 的扩展实现,所以,在并发修改期间,相应 Segment 是被锁定的。

  • 在最初阶段,进行重复性的扫描,以确定相应 key 值是否已经在数组里面,进而决定是更新还是放置操作。

  • 在 ConcurrentHashMap 中解决扩容的问题,不是整体的扩容,而是单独对 Segment 进行扩容。

  • 为了减少锁定segment的开销,ConcurrentHashMap 的实现是通过重试机制(RETRIES_BEFORE_LOCK,指定重试次数 2),来试图获得可靠值。如果没有监控到发生变化(通过对比 Segment.modCount),就直接返回,否则获取锁进行操作。

机制在Java 8 上的变化:

  • 总体结构上,它的内部存储与HashMap 结构非常相似,同样是大的桶(bucket)数组,然后内部也是一个个所谓的链表结构(bin),同步的粒度要更细致一些。

  • 其内部仍然有 Segment 定义,但仅仅是为了保证序列化时的兼容性而已,不再有任何结构上的用处。

  • 因为不再使用 Segment,初始化操作大大简化,修改为 lazy-load 形式,这样可以有效避免初始开销。

  • 数据存储利用 volatile 来保证可见性。

  • 使用 CAS (Compare And Swap)等操作,在特定场景进行无锁并发操作。

  • 使用 Unsafe、LongAdder 之类底层手段,进行极端情况的优化。

看看在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的示例分析”有了一定的了解,如果想了解更多相关知识,欢迎关注编程网精选频道,感谢各位的阅读!

--结束END--

本文标题: Java源码解析之ConcurrentHashMap的示例分析

本文链接: https://www.lsjlt.com/news/278161.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java源码解析之ConcurrentHashMap的示例分析
    小编给大家分享一下Java源码解析之ConcurrentHashMap的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!早期 ConcurrentHashMap,其实现是基于:分离锁,也就是将内部进行分段(Segme...
    99+
    2023-06-15
  • Java源码ConcurrentHashMap的示例分析
    小编给大家分享一下Java源码ConcurrentHashMap的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一、记录形式打算直接把过程写在源码中,会按序进行注释,查阅的时候可以按序号只看注释部分二、Concur...
    99+
    2023-06-15
  • Java源码解析之ConcurrentHashMap
    早期 ConcurrentHashMap,其实现是基于: 分离锁,也就是将内部进行分段(Segment),里面则是 HashEntry 的数组,和 HashMap 类似,哈...
    99+
    2022-11-12
  • Java ConcurrentHashMap源码分析
    这篇“Java ConcurrentHashMap源码分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Java&...
    99+
    2023-07-05
  • Java ConcurrentHashMap的源码分析详解
    目录概述ForwardingNode节点TreeNodeTreeBinSizeCtl初始化初始化流程查找插入扩容红黑树的读&写读操作写操作小结容器计数总结概述 Concurr...
    99+
    2023-03-02
    Java ConcurrentHashMap源码 Java ConcurrentHashMap
  • JAVA核心知识之ConcurrentHashMap源码分析
    1 前言 ConcurrentHashMap是基于Hash表的Map接口实现,键与值均不允许为NULL,他是一个线程安全的Map。同时他也是一个无序的Map,不同时间进行遍历可能会得...
    99+
    2022-11-12
  • Java源码解析之接口Collection的示例分析
    小编给大家分享一下Java源码解析之接口Collection的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、图示二、方法定义我们先想一想,公司如果要我...
    99+
    2023-06-15
  • 解析ConcurrentHashMap: put方法源码分析
    上一章:预热(内部一些小方法分析) put()方法是并发HashMap源码分析的重点方法,这里涉及到并发扩容,桶位寻址等等… JDK1.8 ConcurrentHa...
    99+
    2022-11-12
  • JDK1.8中的ConcurrentHashMap源码分析
     一、容器初始化 1、源码分析 在jdk8的ConcurrentHashMap中一共有5个构造方法,这四个构造方法中都没有对内部的数组做初始化, 只是对一些变量的初始值做...
    99+
    2022-11-12
  • 解析ConcurrentHashMap: transfer方法源码分析(难点)
    上一篇文章介绍过put方法以及其相关的方法,接下来,本篇就介绍一下transfer这个方法(比较难),最好能动手结合着源码进行分析,并仔细理解前面几篇文章的内容~ ...
    99+
    2022-11-12
  • python源码剖析之PyObject的示例分析
    这篇文章主要介绍python源码剖析之PyObject的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、Python中的对象Python中一切皆是对象。————Guido van Rossum(1989)这...
    99+
    2023-06-15
  • Vue源码分析之虚拟DOM的示例分析
    小编给大家分享一下Vue源码分析之虚拟DOM的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!为什么需要虚拟dom?虚拟DOM就是为了解决浏览器性能问题而被...
    99+
    2023-06-15
  • Spring源码解析之编程式事务的示例分析
    这篇文章主要为大家展示了“Spring源码解析之编程式事务的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Spring源码解析之编程式事务的示例分析”这篇文章吧。一、前言在Spring中...
    99+
    2023-06-15
  • Java并发编程之ConcurrentLinkedQueue源码的示例分析
    这篇文章给大家分享的是有关Java并发编程之ConcurrentLinkedQueue源码的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、ConcurrentLinkedQueue介绍并编程中,一般需...
    99+
    2023-06-15
  • java中CopyOnWriteArrayList源码的示例分析
    这篇文章将为大家详细讲解有关java中CopyOnWriteArrayList源码的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。简介CopyOnWriteArrayList是ArrayList的...
    99+
    2023-06-29
  • Java中Handler源码的示例分析
    这篇文章主要介绍了Java中Handler源码的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。从很早开始就认识到 Handler 了,只不过那时修为尚浅,了解的不够深...
    99+
    2023-06-02
  • Vue中AST源码解析的示例分析
    这篇文章主要介绍Vue中AST源码解析的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!从这个函数开始的:// Line-3924  Vue.prototy...
    99+
    2022-10-19
  • Java并发源码分析ConcurrentHashMap线程集合
    目录简介常量构造方法putinitTabletabAtcasTabAthelpTransferputTreeVal锁状态lockRootcontendedLocktreeifyBin...
    99+
    2023-02-01
    Java ConcurrentHashMap Java 线程集合
  • Vue源码解析之数据响应系统的示例分析
    这篇文章主要介绍Vue源码解析之数据响应系统的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!数据双向绑定的思路1. 对象先来看元素是对象的情况。假设我们有一个对象和一个监测方...
    99+
    2022-10-19
  • Spring源码解析之推断构造方法的示例分析
    小编给大家分享一下Spring源码解析之推断构造方法的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Spring推断构造方法贴个测试代码直接开干,这只是个...
    99+
    2023-06-15
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作