并发编程-JMM

首先我们要知道java内存模型

1、 内存模型 这个概念。我们可以理解为:在特定的操作协议下,对特定的内 存或高速缓存进行读写访问的过程抽象。不同架构的物理计算机可以有不一样 的内存模型,JVM 也有自己的内存模型。

2、JVM 中试图定义一种 Java 内存模型(Java Memory Model, JMM)来屏蔽各 种硬件和操作系统的内存访问差异,以实现让 Java 程序 在各种平台下都能达到 一致的内存访问效果

3、从开发者角度而言,Java内存模型描述了在多线程代码中哪些行为是合法 的,以及线程如何通过内存进行交互。它描述了“程序中的变量“ 和 ”从内存或者 寄存器获取或存储它们的底层细节”之间的关系。 Java 内存模型规范了 JVM 如何提供按需禁用缓存和编译优化的方法。具体来 说,这些方法包括 volatile、synchronized 和 final 三个关键字,以及 Happens-Before 规则

主内存与工作内存

JMM 的主要目标是 定义程序中各个变量的访问规则,即在虚拟机中将变量存储 到内存和从内存中取出变量这样的底层细节。此处的变量(Variables)与 Java 编程中所说的变量有所区别,它包括了实例字段、静态字段和构成数值对象的 元素,但不包括局部变量与方法参数,因为后者是线程私有的,不会被共享, 自然就不会存在竞争问题。 JMM 规定了所有的变量都存储在主内存(Main Memory)中。 每条线程还有自己的工作内存(Working Memory),工作内存中保留了该线 程使用到的变量的主内存的副本。工作内存是 JMM 的一个抽象概念,并不真实 存在,它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的 变量。不同的线程间也无法直接访问对方工作内存中的变量,线程间变量值的 传递均需要通过主内存来完成。

 

!!!为了获得较好的执行效能,

  1. JMM 并没有限制执行引擎使用处理器的特定寄存器或缓存来和主存进 行交互,
  2. JMM 也没有限制即时编译器调整指令执行顺序这类优化措施

JMM解决什么问题?

1、工作内存数据一致性:可见性问题 各个线程操作数据时会使用工作内存中的主内存中共享变量副本,当多个线程 的运算任务都涉及同一个共享变量时,可能导致各自的共享变量副本不一致。 如果真的发生这种情况,数据同步回主内存以谁的副本数据为准? Java 内存模型主要通过一系列的数据同步协议、规则来保证数据的一致性。

2、约束指令重排序优化:有序性问题 Java 中重排序通常是编译器或运行时环境为了优化程序性能而采取的对指令进 行重新排序执行的一种手段。重排序可分为两类:编译期重排序和运行期重排 序(处理器乱序优化),分别对应编译时和运行时环境。 同样的,指令重排序不是随意重排序,它需要满足以下几个条件:

  • 在单线程环境下不能改变程序运行的结果。即时编译器(和处理器)需要保 证程序能够遵守 as-if-serial 属性。通俗地说,就是在单线程情况下,要 给程序一个顺序执行的假象。即使经过重排序后的执行结果要与顺序执行的 结果保持一致。
  • 存在数据依赖关系的不允许重排序。
  • 多线程环境下,如果线程处理逻辑之间存在依赖关系,有可能因为指令重排 序导致运行结果与预期不同。

JMM内存交互

JMM 定义了 8 个操作来完成主内存和工作内存之间的交互操作。JVM 实现时必 须保证下面介绍的每种操作都是 原子的(对于 double 和 long 型的变量来说, load、store、read、和 write 操作在某些平台上允许有例外 )。

  • lock (锁定) - 作用于主内存的变量,它把一个变量标识为一条线程独占的 状态。
  • unlock (解锁) - 作用于主内存的变量,它把一个处于锁定状态的变量释放 出来,释放后的变量才可以被其他线程锁定。
  • read (读取) - 作用于主内存的变量,它把一个变量的值从主内存传输到线 程的工作内存中,以便随后的 load 动作使用。
  • load (载入) - 作用于工作内存的变量,它把 read 操作从主内存中得到的变 量值放入工作内存的变量副本中。
  • use (使用) - 作用于工作内存的变量,它把工作内存中一个变量的值传递给 执行引擎,每当虚拟机遇到一个需要使用到变量的值得字节码指令时就会执 行这个操作。
  • assign (赋值) - 作用于工作内存的变量,它把一个从执行引擎接收到的值 赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行 这个操作。
  • store (存储) - 作用于工作内存的变量,它把工作内存中一个变量的值传送 到主内存中,以便随后 write 操作使用。
  • write (写入) - 作用于主内存的变量,它把 store 操作从工作内存中得到的 变量的值放入主内存的变量中。

如果要把一个变量从主内存中复制到工作内存,就需要按序执行 read 和 load 操作;如果把变量从工作内存中同步回主内存中,就需要按序执行 store 和 write 操作。但 Java 内存模型只要求上述操作必须按顺序执行,而 没有保证必须是连续执行。 JMM 还规定了上述 8 种基本操作,需要满足以下规则:

1. read 和 load 必须成对出现;store 和 write 必须成对出现。即不允许一个 变量从主内存读取了但工作内存不接受,或从工作内存发起回写了但主内存 不接受的情况出现。

2. 不允许一个线程丢弃它的最近 assign 的操作,即变量在工作内存中改变了 之后必须把变化同步到主内存中。

3. 不允许一个线程无原因的(没有发生过任何 assign 操作)把数据从工作内 存同步回主内存中。 4. 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被 初始化(load 或 assign )的变量。换句话说,就是对一个变量实施 use 和 store 操作之前,必须先执行过了 load 或 assign 操作。

5. 一个变量在同一个时刻只允许一条线程对其进行 lock 操作,但 lock 操作可 以被同一条线程重复执行多次,多次执行 lock 后,只有执行相同次数的 unlock 操作,变量才会被解锁。所以 lock 和 unlock 必须成对出现。

6. 如果对一个变量执行 lock 操作,将会清空工作内存中此变量的值,在执行 引擎使用这个变量前,需要重新执行 load 或 assign 操作初始化变量的值。

7. 如果一个变量事先没有被 lock 操作锁定,则不允许对它执行 unlock 操作, 也不允许去 unlock 一个被其他线程锁定的变量。

8. 对一个变量执行 unlock 操作之前,必须先把此变量同步到主内存中(执行 store 和 write 操作)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

郎伟学架构

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值