JMM详解:关键字
accttodo 12/31/2023 Java
目录
参考:
# JMM的关键字
# volatile 关键字
很多并发编程都使用了 volatile
关键字,主要的作用包括两点:
- 保证线程间变量的可见性
- 禁止
CPU
进行指令重排序
可见性
volatile
修饰的变量,当一个线程改变了该变量的值,其他线程是立即可见的。普通变量则需要重新读取才能获得最新值。
示例:
public class VolatileSafe {
private volatile boolean shutDown;
public void close() {
shutDown = true;
}
public void doWork(){
while (!shutDown){
System.out.println("safe...");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
在这个例子中,对变量shutDown
的修改是原子性的,它是用关键字volatile
修饰的,修改对其他线程立即可见,保证了线程安全。
对 volatile
变量的修改为什么可以做到立即可见?
- 当写一个
volatile
变量时,JMM 会把对该线程对应的工作内存中的共享变量值刷新到主内存中 - 当读一个
volatile
变量时,JMM 会把该线程对应的工作内存置为无效,使得线程只能从主内存中重新读取共享变量

volatile 一定能保证线程安全吗?
volatile 不能一定能保证线程安全。
示例:
public class VolatileTest extends Thread {
private static volatile int count = 0;
public static void main(String[] args) throws Exception {
Vector<Thread> threads = new Vector<>();
for (int i = 0; i < 100; i++) {
VolatileTest thread = new VolatileTest();
threads.add(thread);
thread.start();
}
//等待子线程全部完成
for (Thread thread : threads) {
thread.join();
}
//输出结果,正确结果应该是1000,实际却是984
System.out.println(count);//984
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
//休眠500毫秒
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
count++;
}
}
}
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
27
28
29
30
31
32
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
27
28
29
30
31
32
可见性不能保证操作的原子性,前面说过了 count++
不是原子性操作,会当做三步:
- 先读取 count 的值,
- 然后 +1,
- 最后赋值回去 count 变量。
需要保证线程安全的话,需要使用 synchronized
关键字 或者 lock
锁,给 count++
这段代码上锁:
private static synchronized void add() {
count++;
}
1
2
3
2
3