zoukankan      html  css  js  c++  java
  • Java 并发学习之 JMM

    Java 并发学习之 JMM

    顺序一致性模型与 JMM

    顺序一致性模型是一种理想的内存模型,在这个模型下,指令是严格按照代码的编写顺序执行,同时所有线程只能看到同一个内存区且对内存区的操作都是互斥的,内存对所有线程都是可见的。

    JMM 中,由于每个线程有自己的工作内存,很多情况下,只是对工作内存中的变量副本进行修改而未真正同步到主内存中,因此每个线程对内存的更改对其他线程都是不可见的,同时出于对性能的优化,指令的顺序经过编译器和处理器的重排,其执行的顺序跟代码的编写顺序很有可能不一样。所以导致了 JMM 不可能像顺序一致性模型那样具有极强的内存可见性保证,在 JMM 中如果要一个操作执行的结果对另一个操作可见,那么这两个操作必须满足 happens-before 原则。

    什么是指令重排?

    指令重排是编译器和处理器用于优化程序性能的手段,举个例子,指令 B 的执行依赖指令 A 的结果,此时有一些对内存的访问操作就可以插入指令 B 和指令 A 之间,以提高 IO 的效率。指令重排有一个基本原则,就是在单线程中遵守数据的依赖关系(写写、写读、读写指令间都是有依赖关系,不允许重排),从而保证单线程中执行结果的一致性。

    本文将从happens-before 原则出发,分析 JMM 中用于提供内存可见性的设计。

    happens-before

    volatile

    volatile 变量具有可见性原子性。可见性是指对一个 volatile 变量的读总是能够看到对这个 volatile 变量的最后写入;原子性是指对 volatile 变量的读操作和写操作具有原子性,复合操作(volatile++)不具有。

    为了保证 volatile 变量的两个特性,JMM 在对 volatile 变量的读操作和写操作进行以下的设计:

    1. 当写一个 volatile 变量时,JMM 会将线程工作内存的变量刷新到主内存。
    2. 当读一个 volatile 变量时,JMM 会将工作内存的变量置为无效,直接从主内存中获取。

    由于指令的重排序导致多个线程下,变量的读写依赖无法被满足,JMM 对 volatile 变量的指令重排做了限制:

    1. volatile 写之前的操作不能被重排到 volatile 写之后。
    2. volatile 读之后的操作不能被重排到 volatile 读之前。
    3. 当第一个操作是 volatile 写,第二个操作是 volatile 读时,不能重排。

    这些限制都是通过 JMM 内存屏障来实现的。内存屏障的细节本文不做详细介绍。

    针对锁的获取到释放过程遵循以下三个 happens-before 关系:

    1. 程序次序规则:先获取锁,再执行临界区代码
    2. 监视器规则:先释放锁,再获取锁
    3. happens-before 传递性:先获取锁的临界区代码先执行

    为了保证内存的可见性,JMM 在锁的释放和获取操作进行以下的设计:

    1. 当线程释放锁时,会把该线程对应的工作内存的变量刷新到主内存。
    2. 当线程获取锁时,会把线程对应的工作内存的变量置为无效,从而使得临界区代码必须从主内存中读取变量。

    JMM 锁的释放和获取操作的可见性实际上是通过对 volatile 变量的操作来实现的。(参考 AQS 的实现)

    final 域

    JMM 对 final 域的重排序也做了限制:

    1. 在构造函数内对 final 域的写入,与随后将被构造对象的引用赋值给一个引用对象,这两个操作不能重排。
    2. 初次读包含 final 域对象的引用,与随后初次读这个 final 域,这两个操作不能重排。

    这些限制也是通过 JMM 内存屏障实现的。

    线程

    在线程 A 中执行操作 ThreadB.start(),A 线程中的 ThreadB.start() 操作 happens-before 线程 B 中的任意操作。在线程 A 中执行操作 ThreadB.join(),B 线程中的任意操作 happens-before 线程 A 从 Thread.join() 操作返回。

  • 相关阅读:
    2014 Super Training #7 C Diablo III --背包问题(DP)
    2014 Super Training #7 E Calculate the Function --矩阵+线段树
    2014 Super Training #7 B Continuous Login --二分
    2014 Super Training #10 G Nostop --矩阵快速幂
    2014 Super Training #10 D 花生的序列 --DP
    2014 Super Training #10 C Shadow --SPFA/随便搞/DFS
    2014 Super Training #6 F Search in the Wiki --集合取交+暴力
    2014 Super Training #6 G Trim the Nails --状态压缩+BFS
    2014 Super Training #9 F A Simple Tree Problem --DFS+线段树
    2014 Super Training #8 G Grouping --Tarjan求强连通分量
  • 原文地址:https://www.cnblogs.com/bdsir/p/8719550.html
Copyright © 2011-2022 走看看