内存模型

什么是JMM

1
2
3
4
JMM Java Memory Model,
它定义了
主存(共享内存)、工作内存(线程私有)抽象概念,
底层对应着 CPU 寄存器、缓存、硬件内存、 CPU 指令优化等。

JMM主要体现在以下几个方面

原子性

1
保证指令不会受到线程上下文切换的影响

引例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Atomicity {
static int i = 0;
static Object object = new Object();

public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
synchronized (object) {
for (int i1 = 0; i1 < 50000; i1++) {
i++;
}
}
});
Thread t2 = new Thread(() -> {
synchronized (object) {
for (int i1 = 0; i1 < 50000; i1++) {
i--;
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}

理解

image-20220114133044894

可见性

1
保证指令不会受 cpu 缓存的影响

问题引例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Visibility {

static boolean run = true;

public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (run) {
// ...
}
});
t.start();
Thread.sleep(1000);
run = false; // 代码最终并不会停下来,因为t线程要频繁从主内存中读取run的值,JIT编译器会将run的值缓存至自己工作内存中的高速缓存中,减少对主存中run的访问,提高效率
}
}

volatile 关键字

1
2
3
它可以修饰成员变量和静态成员变量,
用来避免线程从自己的工作缓存中查找变量的值,
必须到主存中获取它的值,线程操作 volatile 变量都是直接操作主存

理解

1
2
一个线程对 volatile 变量的修改对另一个线程而言是可见的,
不能保证原子性,仅用在一个写线程,多个读线程的情况。

关于 synchronized 关键字

1
2
synchronized 语句块既可以保证代码块的原子性,同时也能保证代码块内变量的可见性。
但缺点是 synchronized 属于重量级操作,性能相对较低

用例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Visibility {

static boolean run = true;

public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (run) {
// ...
System.out.println(1); // 为什么使用sout语句也能将线程停下来?
}
});
t.start();
Thread.sleep(1000);
run = false;
}
}

解释

1
2
3
4
5
6
public void println(int x) {
synchronized (this) { // 因为sout语句内部使用了 synchronized 语句块
print(x);
newLine();
}
}

有序性

1
保证指令不会受 cpu 指令并行优化的影响

引例

image-20220114150908935 image-20220114150942383
1
出现 0 的情况的现象叫做指令重排,是 JIT 编译器在运行时的一些优化,这个现象需要通过大量测试才能复现

解决指令重排

1
为 ready 参数添加 volatile 关键字修饰,确保可见性,避免指令重排

理解

image-20220114152704260

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!