Java 并发编程理论基础 1
Java 内存模型
Java 并发三大问题
可见性
私有缓存导致数据具有多个可能
解决办法
禁用缓存(按需)
原子性
高级语言一条语句对应多条CPU指令 线程切换的时间分片粒度为一条CPU指令
解决办法
单核
控制线程切换的时间
多核
保证对共享变量的修改互斥
有序性
编译优化导致的指令重排序
解决办法
禁用编译优化(按需)
java 内存模型通过 volatile synchronized 和 final 三个关键字, 以及 6 项 Happens-Before 规则, 提供按需禁用缓存和编译优化
Happens-Before 规则
含义(重要)
前面一个操作的结果对后续操作是可见的.
Happens-Before 约束了编译器的优化行为, 虽允许编译器优化, 但是要求编译器优化后一定要遵守 Happens-Before 规则.
Happens-Before 规则和程序员相关的一共有一下6项, 都是关于可见性的.
-
程序的顺序性规则 在一个线程中, 按照程序的顺序, 前面的操作 Happens-Before 后面的操作.
-
volatile 变量规则 对一个 volatile 变量的写操作, Happens-Before 后续对这个 volatile 变量的读操作.
-
传递性 如果 A Happens-Before B, 且 B Happens-Before C, 那么 A Happens-Before C.
- 管程中锁的规则
一个锁的解锁 Happens-Before 后续对这个锁的加锁
管程 管程是一种通用的同步原语, 在java 中指的是 synchronized, synchronized 是 java 里对管程的实现.
-
线程 start() 规则 主线程 A 启动子线程 B 后, 子线程 B 能够看到主线程在启动子线程 B 前的操作.
-
线程 join() 规则 主线程 A 等待子线程 B 完成(主线程通过调用子线程 B 的 join() 方法实现), 当子线程 B 完成后(主线程 A 中 join() 方法返回), 主线程能够看到子线程操作.
-
线程中断规则 对线程 interrupt() 方法的调用 Happens-Before 被中断线程的代码检测到中断事件的发送, 可以通过 Thread.interrupted() 方法检测到是否有发送中断.
- 对象终结规则 一个对象的初始化完成 Happens-Before 它的 finalize() 方法的开始.
final
1.5 版本之后 Java 内存模型对 final 类型变量的重排序进行了约束.只要我们提供正确构造函数没有”逸出”, 就不会出问题.