并发的根源
1源头之一:缓存导致的可见性问题
一个线程对共享变量的修改,另外一个线程能够立刻看到,我们称为可见性
2源头之二:线程切换带来的原子性问题
把一个或者多个操作在 CPU 执行的过程中不被中断的特性称为原子性
3源头之三:编译优化带来的有序性问题
JVM 提供按需禁用缓存和编译优化的方法。包括 volatile、synchronized 和 final 三个关键字,以及六项 Happens-Before 规则。
Volatile :禁用 CPU 缓存,告诉编译器,对这个变量的读写,不能使用 CPU 缓存,必须从内存中读取或者写入
Java 内存模型在 1.5 版本对 volatile 语义进行了增强:Happens-Before 规则。
Happens-Before 规则:前面一个操作的结果对后续操作是可见的
1. 程序的顺序性规则
// 以下代码来源于【参考 1】
class VolatileExample {
int x = 0;
volatile boolean v = false;
public void writer() {
x = 42;
v = true;
}
public void reader() {
if (v == true) {
// 这里 x 会是多少呢? 42
}
}
}
程序前面对某个变量的修改一定是对后续操作可见的
2. volatile 变量规则
就是如果一个线程先去写一个volatile变量,然后一个线程去读这个变量,那么这个写操作的结果一定对读的这个线程可见
3.传递性
3 并发编程的 Bug 根源:
可见性、原子性、有序性
导致可见性的原因是缓存,导致有序性的原因是编译优化
4 需禁用缓存和编译优化的方法 volatile、synchronized 和 final 六项 Happens-Before 规则
4.1 volatile 关键字并不是 Java 语言的特产,古老的 C 语言里也有,它最原始的意义就是禁用 CPU 缓存
我们声明一个 volatile 变量 volatile int x = 0,它表达的是:告诉编译器,对这个变量的读写,不能使用 CPU 缓存,必须从内存中读取或者写入