volatile深度解析
# 简介
根据java内存模型(JMM),区分共享内存区域和线程私有内存区域,线程对变量的修改不会马上同步到其他线程,所以在多线程间对公共变量修改,其他线程不会马上读到最新的数据。volatile
关键字就是为了解决多线程间共享变量的可见性,只要在共享变量前加了volatile
关键字,某线程修改公共变量后,其他线程立刻读到最新数据。
# 可见性原理
我们先看一段java代码示例
public class Test1 {
public static int a = 1;
public int b = 2;
public volatile int c = 3;
public final int d = 4;
private String s = "5";
private Object o = new Object();
public static void main(String[] args) {
test();
}
public static void test() {
System.out.println("1");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Javap命令查看字节码文件, javap -v Test1.class
。
// 略。。
// 自看最关键的地方,加了volatile关键字的会有 ACC_VOLATILE标识
public volatile int c;
descriptor: I
flags: ACC_PUBLIC, ACC_VOLATILE
1
2
3
4
5
2
3
4
5
在JVM层面,如果发现ACC_VOLATILE标识,在写改字段的时候会添加lock前缀指令:lock cmpxchg %rdi,(%rdx)
, 该指令的作用:
- 该指令会将当前缓存数据回写到内存中。
- 其他处理器的缓存失效
在早期的CPU,lock指令会锁总线,其他线程操作会阻塞,直到锁释放。现在比较新的CPU都是锁缓存,再结合缓存一致性(MESI)来保证数据同步。
# 禁止指令重排序
为了提高性能,JMM在保证不改变语义的情况下,可能会对指令进行重排序,添加了volatile关键字会阻止这种优化。
# 附录
# IDEA添加javap命令
命名: C:\dev\Java\jdk-1.8\bin\javap.exe
参数:-v $OutputPath$\$FileDirRelativeToSourcepath$\$FileNameWithoutAllExtensions$.class
工作目录: $ProjectFileDir$
上次更新: 2024/04/10, 16:41:50