什么是Java内存模型?
简单来说,Java 内存模型规范了 JVM 如何提供按需禁用缓存和禁用编译优化的方法。它是用来解决cpu缓存导致的可见性问题和编译优化带来的有序性问题 具体来说,这些方法包括 volatile、synchronized 和 final 三个关键字,以及六项 Happens-Before 规则
先来看一段示例代码
public class VolatileExample {
int x = 0;
volatile boolean v = false;
public void writer() {
x = 42;
v = true;
}
public void reader() {
if (v == true) {
// 如果一个线程write()之后,另外的线程读到 x 会是多少?1.5之前可能读取为0,这个问题在【传递性】解决了
}
}
}
Happens-Before规则
Happens-Before 约束了编译器的优化行为,虽允许编译器优化,但是要求编译器优化后一定遵守 Happens-Before 规则。
表达意思:前面操作的结果对于后续的操作是可见的。即如果多个线程,某个线程在操作时,如果满足了下列规则,那么必须保证其他线程对于这些规则对应的操作结果不应该在缓存中。
1. 程序的顺序规则
- 解释:一个线程中,按照程序编写的顺序,前面的操作Happens-Before后续的任意操作
- 解读:符合正常思维,即前面对变量做的修改对于后续操作是可见的
2. volatile 变量规则
- 解释:一个volatile变量的写操作,Happens-Before于后续对volatile变量的写操作。
- volatile关键字作用:避免cpu缓存和指令优化重排
- 解读:某个线程在读取一个volatile变量时,会去检查之前是否有其他线程改变了值但却没有写入内存的
3. 传递性
- 解释:A Happpens-Before B,且 B Happens-Before C。那么A一定Happens-Before C
- 解读:通过这个,增强了volatile语义,解决了示例程序问题
4. 管程中锁的规则
- 解释: 将一个锁的解锁 Happens-Before 后续对于这个锁的加锁
- 解读:synchronized是Java里对管程的实现,这个规则即加锁时需要判断处于解锁状态
5. 线程start()规则
- 解释: 主线程 A 启动子线程 B 后,子线程 B 能够看到主线程在启动子线程 B 前的操作。
- 解读:
4. 线程join()规则
- 解释: 主线程在join()调用之前的操作结果,必须对于join的线程是可见的。同样的,在join之后,join线程内部对于共享变量的操作,主线程也是可见的。
- 解读: