java重排序_Java synchronized 能防止指令重排序吗?

@ZealTalk 说的是 synchronized 可以防止指令重排,这个观点不对的,也欢迎回答的各位来讨论

synchronized 的有序性

来讨论这个问题先,先看看 Java 里的操作无序现象是什么:《深入理解 Java 虚拟机》- P374:如果在一个线程观察另一个线程,所有操作都是无序的指的是 “指令重排序” 和 “工作内存与主内存同步延迟” 现象

Java 里只有 volatile 变量是能实现禁止指令重排的

synchronized 虽然不能禁止指令重排,但也能保证有序性?

这个有序性是相对语义来看的,线程与线程间,每一个 synchronized 块可以看成是一个原子操作,它保证每个时刻只有一个线程执行同步代码,它可以解决上面引述的工作内存和主内存同步延迟现象引发的无序

所以,synchronized 和 volatile 的有序性与可见性是两个角度来看的:synchronized 是因为块与块之间看起来是原子操作,块与块之间有序可见

volatile 是在底层通过内存屏障防止指令重排的,变量前后之间的指令与指令之间有序可见

同时,synchronized 和 volatile 有序性不同也是因为其实现原理不同:synchronized 靠操作系统内核互斥锁实现的,相当于 JMM 中的 lock 和 unlock。退出代码块时一定会刷新变量回主内存

volatile 靠插入内存屏障指令防止其后面的指令跑到它前面去了

总而言之就是, synchronized 块里的非原子操作依旧可能发生指令重排

volatile 的有序性《Java 并发编程实战》 P287 指出,有 synchronized 无 volatile 的 DCL 会出现的情况:线程可能看到引用的当前值,但对象的状态值确少失效的,这意味着线程可以看到对象处于无效或错误的状态

看看这本翻译地跟翔一样的书的引用,还是去 Stack OverFlow 看看吧....

结合一下就可以知道为何双重校验单例还要加上 volatile 的原因了

因为 new 这个指令是非原子操作,底层是分成几条指令来执行的,加上 volatile 是禁止指令重排,保证别的线程读到的时候一定是状态和引用正常的、一个完整的对象,防止其他线程看到的是类似引用有了,内存资源却还没分配的对象

后记

值得一提的是 javap -v 编译出来的字节码指令还不是全部指令,它里面的 new 指令还是能更细分的,因为 volatile 的内存屏障指令还要深入到汇编的层次插入的

你可能感兴趣的:(java重排序)