ConcurrentHashMap到底是什么后续

Java 内存模型

由于 ConcurrentHashMap 是建立在 Java 内存模型基础上的,为了更好的理解 ConcurrentHashMap,让我们首先来了解一下 Java 的内存模型。

Java 语言的内存模型由一些规则组成,这些规则确定线程对内存的访问如何排序以及何时可以确保它们对线程是可见的。下面我们将分别介绍 Java 内存模型的重排序,内存可见性和 happens-before 关系。

重排序

内存模型描述了程序的可能行为。具体的编译器实现可以产生任意它喜欢的代码 — 只要所有执行这些代码产生的结果,能够和内存模型预测的结果保持一致。这为编译器实现者提供了很大的自由,包括操作的重排序。

编译器生成指令的次序,可以不同于源代码所暗示的“显然”版本。重排序后的指令,对于优化执行以及成熟的全局寄存器分配算法的使用,都是大有脾益的,它使得程序在计算性能上有了很大的提升。

重排序类型包括:

  • 编译器生成指令的次序,可以不同于源代码所暗示的“显然”版本。
  • 处理器可以乱序或者并行的执行指令。
  • 缓存会改变写入提交到主内存的变量的次序。

内存可见性

由于现代可共享内存的多处理器架构可能导致一个线程无法马上(甚至永远)看到另一个线程操作产生的结果。所以 Java 内存模型规定了 JVM 的一种最小保证:什么时候写入一个变量对其他线程可见。

在现代可共享内存的多处理器体系结构中每个处理器都有自己的缓存,并周期性的与主内存协调一致。假设线程 A 写入一个变量值 V,随后另一个线程 B 读取变量 V 的值,在下列情况下,线程 B 读取的值可能不是线程 A 写入的最新值:

  • 执行线程 A 的处理器把变量 V 缓存到寄存器中。
  • 执行线程 A 的处理器把变量 V 缓存到自己的缓存中,但还没有同步刷新到主内存中去。
  • 执行线程 B 的处理器的缓存中有变量 V 的旧值。

Happens-before 关系

happens-before 关系保证:如果线程 A 与线程 B 满足 happens-before 关系,则线程 A 执行动作的结果对于线程 B 是可见的。如果两个操作未按 happens-before 排序,JVM 将可以对他们任意重排序。

下面介绍几个与理解 ConcurrentHashMap 有关的 happens-before 关系法则:

  1. 程序次序法则:如果在程序中,所有动作 A 出现在动作 B 之前,则线程中的每动作 A 都 happens-before 于该线程中的每一个动作 B。
  2. 监视器锁法则:对一个监视器的解锁 happens-before 于每个后续对同一监视器的加锁。
  3. Volatile 变量法则:对 Volatile 域的写入操作 happens-before 于每个后续对同一 Volatile 的读操作。
  4. 传递性:如果 A happens-before 于 B,且 B happens-before C,则 A happens-before C。

介绍一下Volatile

Volatile是轻量级的synchronized。Volatile提供了可见性。而synchronized提供了可见性和一致性。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。


用 Volatile 变量协调读写线程间的内存可见性

由于内存可见性问题,未正确同步的情况下,写线程写入的值可能并不为后续的读线程可见。

下面以写线程 M 和读线程 N 来说明 ConcurrentHashMap 如何协调读 / 写线程间的内存可见性问题。

图 6. 协调读 – 写线程间的内存可见性的示意图:

图 6. 协调读 - 写线程间的内存可见性的示意图:

假设线程 M 在写入了 volatile 型变量 count 后,线程 N 读取了这个 volatile 型变量 count。

根据 happens-before 关系法则中的程序次序法则,A appens-before 于 B,C happens-before D。

根据 Volatile 变量法则,B happens-before C。

根据传递性,连接上面三个 happens-before 关系得到:A appens-before 于 B; B appens-before C;C happens-before D。也就是说:写线程 M 对链表做的结构性修改,在读线程 N 读取了同一个 volatile 变量后,对线程 N 也是可见的了。

虽然线程 N 是在未加锁的情况下访问链表。Java 的内存模型可以保证:只要之前对链表做结构性修改操作的写线程 M 在退出写方法前写 volatile 型变量 count,读线程 N 在读取这个 volatile 型变量 count 后,就一定能“看到”这些修改。

ConcurrentHashMap 中,每个 Segment 都有一个变量 count。它用来统计 Segment 中的 HashEntry 的个数。这个变量被声明为 volatile。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值