`
ykdsg
  • 浏览: 17146 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

ConcurrentHashMap 源码分析 (一)

 
阅读更多

很早就想研究ConcurrentHashMap ,不过一直拖拉,我也是个很容易被新奇好玩的技术吸引的人,这个呢有好也有坏。废话不多说上干货。

ConcurrentHashMap 最重要的就是引入了Segment 的概念,他在自己内部定义了这个Class来管理数据,这个Segment 类似于HashMap的定义,因为ConcurrentHashMap 会将对应的读写操作交给Segment。Segment 在ConcurrentHashMap 内部维护一个Segment 数组(默认16个),先将key值的hash值定位到Segment 数组中,取得对应的Segment 之后再利用Segment 的相应方法(对应HashMap里的方法)来读写数据,写操作会加锁。这样做的好处就是,如果有多个线程对ConcurrentHashMap 操作的时候,理想情况下如果key hash到的Segment 是不同的,那么写操作是可以并发执行的。当然像size(),isEmpty(),containsValue(Object value)这些操作涉及到跨Segment 的操作,需要一定的机制来保证,极端情况下需要锁定所有Segment 来做统计。

Segment 主要继承ReentrantLock,在Java5中,ReentrantLock的性能要远远高于内部锁。在Java6中,由于管理内部锁的算法采用了类似于 ReentrantLock使用的算法,因此内部锁和ReentrantLock之间的性能差别不大。
ReentrantLock的构造函数提供了两种公平性选择:创建非公平锁(默认)或者公平锁。在公平锁中,如果锁已被其它线程占有,那么请求线程会加入到等待队列中,并按顺序获得锁;在非公平锁中,当请求锁的时候,如果锁的状态是可用,那么请求线程可以直接获得锁,而不管等待队列中是否有线程已经在等待该锁。公平锁的代价是更多的挂起和重新开始线程的性能开销。在多数情况下,非公平锁的性能高于公平锁。Java内部锁也没有提供确定的公平性保证, Java语言规范也没有要求JVM公平地实现内部锁,因此ReentrantLock并没有减少锁的公平性。在中等或者更高负荷下,ReentrantLock有更好的性能,并且拥有可轮询和可定时的请求锁等高级功能。具体请参考:http://www.blogjava.net/killme2008/archive/2007/09/14/145195.html

有时间的话也研究下ReentrantLock的源码。

Segment源码分析:

可以看到真正存放数据的是HashEntry<K, V>[] table ,这里用到了自定义的class HashEntry ,因为ConcurrentHashMap完全允许多个读操作并发进行,读操作并不需要加锁。如果使用传统的技术,如HashMap中的实现,如果允许可以在hash链的中间删除元素,读操作不加锁将得到不一致的数据。ConcurrentHashMap实现技术是保证HashEntry几乎是不可变的。HashEntry代表每个hash链中的一个节点,其结构如下所示:

可以看到除了value不是final的,其它值都是final的,这意味着不能从hash链的中间或尾部删除节点,因为这需要修改next引用值,所有的节点的修改只能从头部开始。对于put操作,可以一律添加到Hash链的头部。但是对于remove操作,可能需要从中间删除一个节点,这就需要将要删除节点的前面所有节点整个复制一遍,最后一个节点指向要删除结点的下一个结点。这在讲解删除操作时还会详述。为了确保读操作能够看到最新的值,将value设置成volatile,这避免了加锁。

这里读取采用了加锁的方式,注释里也提到了原因。关于逃逸分析可以参考http://blog.csdn.net/ykdsg/archive/2011/03/17/6255618.aspx

其他的操作像:containsKey,containsValue,clear基本上也是采用get这样的循环方式。这里就不具体分析了。接下来看下put方法,因为涉及到扩容操作。

其中关键的地方是rehash()方法和++modCount;这个操作,后面会讲到为什么要维护modCount(在改变元素个数的情况下++)。

可以看到rehash()方法就是把table数组扩容一倍,再把原来的引用计算出在新数组的位置e.hash & sizeMask ,然后放过去,原来旧的table数组就交给垃圾回收。

remove方法其实就是需要重新建立next链,所以需要复制。

基本上Segment特殊一点的方法就上面几个了。

接下来看ConcurrentHashMap是怎么使用的。因为ConcurrentHashMap做了二次hash,一些方法像entrySet()方法就要重写了。

其中HashIterator 的方法advance会循环Segment和其中的table数据,并分别记录下标,下次会在原来的下标继续循环。

构造函数

就是初始化一些基本的值,初始化好Segment数组,接下来的几个构造函数都是调用这个,只是提供了一些初始值,默认的Segment数组长度是16,装载因子是0.75f,初始容量是16。因为构造好Map之后,Segment数组是不会扩容的,如果要放的数据比较多的话,传入比较大的concurrencyLevel 可以支持比较好的并发性。

现在看下get方法的实现

很简单,就是先根据hash找到对应的segment ,然后再调用segment 的get方法。其他的像put ,containsKey,replace 等这些值涉及到单个segment 的操作都是类似的。

下面看下涉及到跨段操作的几个方法

可以看到跨Segment的操作中,先是是不锁表的,但是在多线程的情况下,就会造成数据的不一致,这里就用到了Segment中的modCount来做比较,如果modCount有变化就说明被其他线程污染了,就需要重新做统计,这个时候也是不带锁的。但是这样的循环不可能无限进行下去,所以做了限制,在不带锁的情况下允许进行2次尝试,如果还是受到其他线程的污染,那就要加锁统计了。注意要顺序的加锁再顺序的解锁,不然可能会出现死锁。containsValue的是实现与size相似。

本文基于java6 的源码分析,对一些英文的翻译不一定很到位,一些理解上也存在偏颇,欢迎大家指正。

分享到:
评论

相关推荐

    ConcurrentHashMap源码分析

    ### ConcurrentHashMap源码分析 #### 一、概述 `ConcurrentHashMap`是Java中用于实现线程安全的哈希表的一种高效实现方式。自Java 5引入`java.util.concurrent`包后,`ConcurrentHashMap`成为了多线程环境中推荐...

    ConcurrentHashMap源码分析源码分析

    ConcurrentHashMap源码分析源码分析 代码解释非常详细!!!!

    ConcurrentHashmap源码

    源码分析见我博文:http://blog.csdn.net/wabiaozia/article/details/50684556

    Java并发系列之ConcurrentHashMap源码分析

    Java并发系列之ConcurrentHashMap源码分析 ConcurrentHashMap是Java中一个高性能的哈希表实现,它解决了HashTable的同步问题,允许多线程同时操作哈希表,从而提高性能。 1. ConcurrentHashMap的成员变量: ...

    第二章 ConcurrentHashMap源码分析(JDK8版本)1

    在Java并发编程领域,`ConcurrentHashMap`是一个至关重要的类,它提供了线程安全的哈希映射功能,且性能优异。在JDK8中,`ConcurrentHashMap`的实现方式与之前的版本(如JDK6)有了显著变化,去除了Segment锁段的...

    ConcurrentHashMap源码解析

    通过以上分析,我们可以看到ConcurrentHashMap如何通过锁分段技术来解决HashMap在并发环境下的线程安全问题,并通过巧妙的设计减少线程间锁的竞争,从而提升性能。因此,在设计需要高并发性能的程序时,...

    程序员面试加薪必备:ConcurrentHashMap底层原理与源码分析深入详解

    程序员面试加薪必备_ConcurrentHashMap底层原理与源码分析深入详解

    android开源框架源码分析

    "Android 开源框架源码分析" Android 是一个开源的操作系统,而其框架源码的分析则是其中一个非常重要的方面。今天,我们将对 Android 开源框架源码进行分析,涉及的内容包括 EventBus、Glide、OkHttp、Android ...

    JUC并发编程与源码分析视频课.zip

    《JUC并发编程与源码分析视频课》是一门深入探讨Java并发编程的课程,主要聚焦于Java Util Concurrency(JUC)库的使用和源码解析。JUC是Java平台提供的一组高级并发工具包,它极大地简化了多线程编程,并提供了更...

    高薪程序员面试题精讲系列49之说说ConcurrentHashMap#put方法的源码及数。。。.pdf,这是一份不错的文件

    本文将对ConcurrentHashMap#put方法的源码进行详细分析,从而帮助读者更好地理解ConcurrentHashMap的工作机理。 一、ConcurrentHashMap的底层原理 ConcurrentHashMap是基于哈希表实现的,可以存储大量的数据。其...

    JDK1.8中ConcurrentHashMap中computeIfAbsent死循环bug.docx

    在JDK 1.8版本中,`ConcurrentHashMap`中的`computeIfAbsent`方法存在一个潜在的死循环问题。这个bug主要出现在并发操作时,当`ConcurrentHashMap`需要进行扩容并且`computeIfAbsent`正在执行计算的过程中,可能会...

    集合框架源码分析

    2. **源码分析:ArrayList** `ArrayList`是基于动态数组实现的列表,其内部维护了一个Object类型的数组。当我们添加元素时,如果数组已满,会自动扩容。扩容策略通常是将容量扩大到原来的1.5倍,这在源码中可以通过...

    ConcurrentHashmaq源码分析.txt

    ConcurrentHashMap理论概述,实现原理,简单的源码分析,put和get的简单学习

    免费开源-【Java学习+面试指南】部分内容大部分是Java程序员所需要掌握的核心知识

    ArrayList核心源码+扩容机制分析LinkedList核心源码分析HashMap核心源码+底层数据结构分析ConcurrentHashMap核心源码+底层数据结构分析LinkedHashMap核心源码分析CopyOnWriteArrayList核心源码分析...

    Java源码分析及个人总结

    Java源码分析是软件开发过程中一个重要的学习环节,它能帮助开发者深入理解代码背后的逻辑,提升编程技巧,以及优化程序性能。在这个过程中,我们通常会关注类的设计、算法的应用、数据结构的选择,以及如何利用Java...

    Java源码解析ConcurrentHashMap的初始化

    Java源码解析ConcurrentHashMap的初始化 在Java并发编程中,ConcurrentHashMap是一个非常重要的数据结构,它可以在多线程环境下提供高效的哈希表操作。今天,我们将深入探讨ConcurrentHashMap的初始化过程,并分析...

    java并发源码分析之实战编程

    "java并发源码分析之实战编程"这个主题深入探讨了Java平台上的并发处理机制,旨在帮助开发者理解并有效地利用这些机制来提高程序性能和可扩展性。在这个专题中,我们将围绕Java并发库、线程管理、锁机制、并发容器...

    concurrenthashmap1.7.docx

    本文主要分析`ConcurrentHashMap 1.7`的`put`方法及其涉及到的关键技术。 首先,`put`方法的执行流程要求`key`不能为`null`,否则会抛出`NullPointerException`。根据`key`的哈希值计算出`segments`数组的对应下标`...

    数据库连接池BoneCP源码分析报告

    BoneCP是一款高效、轻量级的Java数据库连接池实现,它的源码分析对于我们理解数据库连接池的工作原理,优化数据库性能以及进行二次开发具有重要意义。 首先,我们需要了解数据库连接池的基本概念。数据库连接池是...

Global site tag (gtag.js) - Google Analytics