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

ConcurrentHashMap 源码分析 (二)

 
阅读更多

本文参考:http://www.javaeye.com/topic/344876及后续评论。

接上文,CurrentHashMap的containsValue 方法在第一层for循环的时候读取了count,但是并没用到这个变量, int c = segments[i].count;这是因为segment[i].count是对volatile变量的访问,接下来segments[i].modCount才能得到几乎最新的值。写volatile变量和它之前的读写操作是不能reorder的,读volatile变量和它之后的读写操作也是不能reorder的。
修改modCount发生在修改count之前,由于count是volatile变量,修改modCount不能和写count的操作reorder,读取count和它之后的操作,比如读取modCount,不能reorder。有了这两个不能reorder才能保证读取了count之后,能读到线程在写count之前的写入的modCount值,这个modCount值是几乎最新的。
如果在读modCount之前不读count,读modCount甚至可能会reorder到写modCount之前。

用reorder解释总是太复杂了,不如用happens-before来得简洁。当一个线程I对count的读时,它读到的值必定是另一个线程,假设是线程II,最近对count的写。这个两个操作存在happens-before关系,即线程II对count的写happens-before线程I对count的读,记作:II:W(count) < I:R(count)。单线程的happens-before规则,又有II:W(modCount) < II:(count)(查看源代码会发现在写count之前必定有写modCount),以及 I:R(count) < I:R(modCount),根据传递规则有,II:(modCount) < I:(modCount),这就是说线程I至少能够读取到线程II写count之前的modCount值。这里有一篇关于happens-before的文章,http://www.javaeye.com/topic/260515。 接下来学习下。

还有就是segment中的get方法,get操作不需要锁。第一步是访问count变量,这是一个volatile变量,由于所有的修改操作在进行结构修改时都会在最后一步写count变量,通过这种机制保证get操作能够得到几乎最新的结构更新。对于非结构更新,也就是结点值的改变,由于HashEntry的value变量是volatile的,也能保证读取到最新的值。接下来就是对hash链进行遍历找到要获取的结点,如果没有找到,直接访回null。对hash链进行遍历不需要加锁的原因在于链指针next是final的。但是头指针却不是final的,这是通过getFirst(hash)方法返回,也就是存在table数组中的值。这使得getFirst(hash)可能返回过时的头结点,例如,当执行get方法时,刚执行完getFirst(hash)之后,另一个线程执行了删除操作并更新头结点,这就导致get方法中返回的头结点不是最新的。这是可以允许,通过对count变量的协调机制,get能读取到几乎最新的数据,虽然可能不是最新的。要得到最新的数据,只有采用完全的同步。但是get能够看到调用get方法之前所有已经完成的put(因为如果发生结构性改变它会写count),虽然可能看不到正在进行的put操作(还没有写count)。这是一个很小的时间窗口,这不可避免,因为get没有同步。这并不是说get不安全,即使get操作时,其它线程在put,get也能看到一致的hash链(与HashMap相比较)。在HashMap中,假设put方法是同步的,get没有同步,get有可能看到实际上从不存在hash链。

get方法总是返回最近put过的数据,这句话说得很别扭,却是很重要的。假设其它线程先后调用了put("a", 1),put("a", 2),put("a", 3),这里能够使用“先后”,是因为put方法是同步的,put方法是排序的,每一个时刻只能够发生一个put方法。接着一个线程调用get("a")方法,并且另一个线程正在调用put("a", 4)方法,那么get("a")会返回3或者4,完全取决于("a")访问结点的值(读value)和put("a", 4)写结点的值(写value)哪个先发生(读volatile变量和写volatile总有个先后顺序,读写普通变量就不一定了,它们可能同时发生),这时put不会发生结构性更新。如果在调用get("a")时,另一个线程正在调用remove("a"),get方法要么返回3,要么返回null,这取决于get方法中getFirst(hash)返回的到底是remove("a")之前还是之后的头结点,而绝不会返回1, 2或任何其它的值。

所以CurrentHashMap并不能保证100%的一致性,只能在性能和一致性之间达到一个比较好的平衡,如果要100%的一致估计还是要用Collections.synchronizedMap(Map map)

分享到:
评论

相关推荐

    ConcurrentHashMap源码分析源码分析

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

    ConcurrentHashMap源码分析

    ### ConcurrentHashMap源码分析 #### 一、概述 `ConcurrentHashMap`是Java中用于实现线程安全的哈希表的一种高效实现方式。自Java 5引入`java.util.concurrent`包后,`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底层原理与源码分析深入详解

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

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

    android开源框架源码分析

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

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

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

    ConcurrentHashmaq源码分析.txt

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

    集合框架源码分析

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

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

    我们首先来理解`computeIfAbsent`方法的基本概念,然后再深入分析这个问题的成因及解决方案。 `computeIfAbsent`是JDK 1.8中新增的一个功能强大的方法,它的作用是在给定的键不存在于映射中时,通过提供的函数来...

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

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

    Java源码分析及个人总结

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

    Java源码解析ConcurrentHashMap的初始化

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

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

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

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

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

Global site tag (gtag.js) - Google Analytics