【Java 并发笔记】volatile 相关整理
本文中其他相关概念可看 【Java 并发笔记】并发机制底层实现整理 文章中说明。
1. hsdis
通过 hsdis 可以查看 Java 编译后的机器指令。
下载后拷贝至 $JAVA_HOME/jre/lib/amd64/server 目录下。
下载后拷贝至 $JAVA_HOME\jre\bin\server 目录下。
window 32/64 位,可以下载 hsdis-amd64.dll/hsdis-i386.dll
或者 linxu 下载 hsdis-amd64.so。
拷贝完成后,运行 java -XX:+UnlockDiagnosticVMOptions。
提示 Could not load hsdis-amd64.dll; library not loadable; PrintAssembly is disabled,则没有加载成功,检查文件名称是否正确。
或者设置 export LD_LIBRARY_PATH=$JAVA_HOME/jre/lib/amd64/server/
安装成功后提示。
......
-agentpath:<pathname>[=<options>]
load native agent library by full pathname -javaagent:<jarpath>[=<options>]
load Java programming language agent, see java.lang.instrument -splash:<imagepath>
show splash screen with specified image
See http://www.oracle.com/technetwork/java/javase/documentation/index.html for more details. 0x00007fd34511a20f: mov %rsp,%rdx 0x00007fd34511a212: and $0xfffffffffffffff0,%rsp 0x00007fd34511a216: callq 0x00007fd35a935a40 ; {runtime_call}
0x00007fd34511a21b: hlt
[Deopt Handler Code]
0x00007fd34511a21c: mov $0x7fd34511a21c,%r10 ; {section_word}
0x00007fd34511a226: push %r10 0x00007fd34511a228: jmpq 0x00007fd345047240 ; {runtime_call}
0x00007fd34511a22d: hlt
0x00007fd34511a22e: hlt
0x00007fd34511a22f: hlt可以运行以下命令生成日志。
Test 为 class 文件名称。
java -XX:+UnlockDiagnosticVMOptions -XX:+TraceClassLoading -XX:+LogCompilation -XX:LogFile=jit.log -XX:-BackgroundCompilation -XX:+PrintAssembly Test
使用 jitwatch 工具,可以帮助分析该日志。
linux 编译 jitwatch 可能会出现缺少 javafx 包的情况,可通过该方法解决。https://chriswhocodes.com/
如果出现 GLIBC_2.14' not found 问题,可通过该方法解决。https://blog.csdn.net/heylun/article/details/78833050
2. volatile
volatile 是一个类型修饰符。volatile 的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略。
2.1 volatile 的特性
保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(实现可见性)
禁止进行指令重排序。(实现有序性)
volatile 只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性。
2.2 volatile 的实现原理
2.2.1 volatile 可见性实现
volatile 变量的内存可见性是基于内存屏障(Memory Barrier)实现。
内存屏障,又称内存栅栏,是一个 CPU 指令。
在程序运行时,为了提高执行性能,编译器和处理器会对指令进行重排序,JMM 为了保证在不同的编译器和 CPU 上有相同的结果,通过插入特定类型的内存屏障来禁止特定类型的编译器重排序和处理器重排序,插入一条内存屏障会告诉编译器和 CPU:不管什么指令都不能和这条 Memory Barrier 指令重排序。
写一段简单的 Java 代码,声明一个 volatile 变量,并赋值。
public class Test {
private volatile int a;
public void update() {
a = 1;
}
public static void main(String[] args) {
Test test = new Test();
test.update();
}}通过 hsdis 和 jitwatch 工具可以得到编译后的汇编代码。
...... 0x0000000002951563: and $0xffffffffffffff87,%rdi 0x0000000002951567: je 0x00000000029515f8 0x000000000295156d: test $0x7,%rdi 0x0000000002951574: jne 0x00000000029515bd 0x0000000002951576: test $0x300,%rdi 0x000000000295157d: jne 0x000000000295159c 0x000000000295157f: and $0x37f,%rax 0x0000000002951586: mov %rax,%rdi 0x0000000002951589: or %r15,%rdi 0x000000000295158c: lock cmpxchg %rdi,(%rdx) //在 volatile 修饰的共享变量进行写操作的时候会多出 lock 前缀的
原创不易,完成人机校验,阅读全文