Java 内存模型

Java 并发三大问题

可见性

私有缓存导致数据具有多个可能

解决办法

禁用缓存(按需)

原子性

高级语言一条语句对应多条CPU指令 线程切换的时间分片粒度为一条CPU指令

解决办法
单核

控制线程切换的时间

多核

保证对共享变量的修改互斥

有序性

编译优化导致的指令重排序

解决办法

禁用编译优化(按需)

java 内存模型通过 volatile synchronized 和 final 三个关键字, 以及 6 项 Happens-Before 规则, 提供按需禁用缓存和编译优化

Happens-Before 规则

含义(重要)

前面一个操作的结果对后续操作是可见的.

Happens-Before 约束了编译器的优化行为, 虽允许编译器优化, 但是要求编译器优化后一定要遵守 Happens-Before 规则.

Happens-Before 规则和程序员相关的一共有一下6项, 都是关于可见性的.

  1. 程序的顺序性规则 在一个线程中, 按照程序的顺序, 前面的操作 Happens-Before 后面的操作.

  2. volatile 变量规则 对一个 volatile 变量的写操作, Happens-Before 后续对这个 volatile 变量的读操作.

  3. 传递性 如果 A Happens-Before B, 且 B Happens-Before C, 那么 A Happens-Before C.

  4. 管程中锁的规则 一个锁的解锁 Happens-Before 后续对这个锁的加锁

    管程 管程是一种通用的同步原语, 在java 中指的是 synchronized, synchronized 是 java 里对管程的实现.

  5. 线程 start() 规则 主线程 A 启动子线程 B 后, 子线程 B 能够看到主线程在启动子线程 B 前的操作.

  6. 线程 join() 规则 主线程 A 等待子线程 B 完成(主线程通过调用子线程 B 的 join() 方法实现), 当子线程 B 完成后(主线程 A 中 join() 方法返回), 主线程能够看到子线程操作.

  7. 线程中断规则 对线程 interrupt() 方法的调用 Happens-Before 被中断线程的代码检测到中断事件的发送, 可以通过 Thread.interrupted() 方法检测到是否有发送中断.

  8. 对象终结规则 一个对象的初始化完成 Happens-Before 它的 finalize() 方法的开始.

final

1.5 版本之后 Java 内存模型对 final 类型变量的重排序进行了约束.只要我们提供正确构造函数没有”逸出”, 就不会出问题.