天天看点

Java内存模型:解决可见性和有序性问题Java内存模型:解决可见性和有序性问题

Java内存模型:解决可见性和有序性问题

01 | Java 内存模型

1、导读

1)可见性、原子性、有序性是并发编程的 Bug 之源

2) Java 内存模型是面试热点,同时也是分析并发问题的基础

3)Java 内存模型是个很复杂的规范,可以从不同的视角来解读

2、Java 内存模型作用

1)解决可见性

2)解决有序性

3、简析出现Java 内存模型的原因

1)缓存和编译优化分别导致可见性和有序性问题,一刀切禁用虽然可以解决问题,但严重影响性能,合理的方案是按需禁用缓存以及编译优化。

2)为了解决可见性和有序性问题,只需要提供给程序员按需禁用缓存和编译优化的方法即可。

4、关键点

1)本质:从程序员视角可以理解为,Java 内存模型规范了 JVM 如何提供按需禁用缓存和编译优化的方法。

2)按需禁用的方法:包括 volatile、synchronized 和 final 三个关键字,以及六项 Happens-Before 规则

02 | 三个关键字:volatile、synchronized、final

1、volatile

1)定义:告诉编译器,对这个变量的读写,不能使用 CPU 缓存,必须从内存中读取或者写入

2)Java 内存模型在 1.5 版本对 volatile 语义进行了增强,Java 内存模型利用Happens-Before 规则,使 volatile 圆满解决了CPU 缓存导致可见性问题

2、synchronized

3、final

1)volatile 表示的是禁用缓存以及编译优化

2)final 修饰变量时,初衷是告诉编译器:这个变量生而不变,可以可劲儿优化

3)使用final要注意“逸出”问题。

03 | 六项 Happens-Before 规则

1、导读

1)Happens-Before 它真正要表达的是:前面一个操作的结果对后续操作是可见的

2)比较正式的说法:Happens-Before 约束了编译器的优化行为,虽允许编译器优化,但是要求编译器优化后一定遵守 Happens-Before 规则

3)Happens-Before 规则中和程序员相关的规则一共有六项,都是关于可见性的。

2、程序的顺序性规则

1)定义:指在一个线程中,按照程序顺序,前面的操作 Happens-Before 于后续的任意操作

2)通俗理解:程序前面对某个变量的修改一定是对后续操作可见的。

3、Volatile 变量规则

1)定义:指对一个 volatile 变量的写操作, Happens-Before 于后续对这个 volatile 变量的读操作

2)关联传递性规则更容易理解。

4、传递性

1)定义:指如果 A Happens-Before B,且 B Happens-Before C,那么 A Happens-Before C

2)图例讲解:

  1. “x=42” Happens-Before 写变量 “v=true” ,这是【程序的顺序性规则】的内容;
  2. 写变量“v=true” Happens-Before 读变量 “v=true”,这是【Volatile 变量规则】的内容 。
  3. 再根据【传递性规则】可得:“x=42” Happens-Before 读变量“v=true”,即线程B能看到“x=42”
Java内存模型:解决可见性和有序性问题Java内存模型:解决可见性和有序性问题

5、管程中锁的规则

1)定义:指对一个锁的解锁 Happens-Before 于后续对这个锁的加锁

2)管程是一种通用的同步原语,在 Java 中指的就是 synchronized,synchronized 是 Java 里对管程的实现

3)管程中的锁在 Java 里是隐式实现的,加锁以及释放锁都是编译器帮我们实现的。

4)例子讲解:线程A通过加锁-修改变量(x由10变为25)-解锁,线程B获得锁后,可以看到线程A修改后的变量值(x=25)。

6、线程 start() 规则

1)定义:它是指主线程 A 启动子线程 B 后,子线程 B 能够看到主线程在启动子线程 B 前的操作

2)通俗来讲,如果线程 A 调用线程 B 的 start() 方法(即在线程 A 中启动线程 B),那么该 start() 操作 Happens-Before 于线程 B 中的任意操作

7、线程 join() 规则

1)定义:指主线程 A 等待子线程 B 完成(主线程 A 通过调用子线程 B 的 join() 方法实现),当子线程 B 完成后(主线程 A 中 join() 方法返回),主线程能够看到子线程的操作

2)通俗来讲,如果在线程 A 中,调用线程 B 的 join() 并成功返回,那么线程 B 中的任意操作 Happens-Before 于该 join() 操作的返回

Java内存模型:解决可见性和有序性问题Java内存模型:解决可见性和有序性问题

04 | 小结

1、导读

1)Java 内存模型里面,最晦涩的部分就是 Happens-Before 规则了

2)在现实世界里,如果 A 事件是导致 B 事件的起因,那么 A 事件一定是先于(Happens-Before)B 事件发生的,这个就是 Happens-Before 语义的现实理解

2、Happens-Before 的语义本质

1)在 Java 语言里面,Happens-Before 的语义本质上是一种可见性

2)定义:A Happens-Before B 意味着 A 事件对 B 事件来说是可见的,无论 A 事件和 B 事件是否发生在同一个线程里

3)例子:A 事件发生在线程 1 上,B 事件发生在线程 2 上,Happens-Before 规则保证线程 2 上也能看到 A 事件的发生

3、Java 内存模型由两部分组成

1)一部分面向编写并发程序的应用开发人员,该部分核心内容是 Happens-Before 规则

2)另一部分面向 JVM 的实现人员

05 | 思维导图

1、Java内存模型思维导图

Java内存模型:解决可见性和有序性问题Java内存模型:解决可见性和有序性问题

参考文献:

[1]王宝令. Java并发编程实战[M]. 极客时间, 2019.

继续阅读