synchronized关键字到底能不能防止指令重排序?

962 阅读2分钟

这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战

先说结论:不行。

指令重排序是什么?

指令重排序:编译器和处理器可以对操作指令进行重排序。

注意这里有一个关键前提:

编译器和处理器必须遵守as-if-serial语义,即不管怎么重排序(编译器和处理器为了提高并行度),单线程程序的执行结果不能被改变。

Java中操作无序现象是什么?

这里引用《深入理解 Java 虚拟机》书中的一句话:

如果在一个线程观察另一个线程,所有操作都是无序的,指的是 “指令重排序” 和 “工作内存与主内存同步延迟” 现象。

有序有什么不同?

我们知道除了synchronized,还有volatile关键字。

synchronized可以保证有序性、可见性、原子性,而volatile只能保证有序性、可见性,不能保证原子性。

等等,怎么它们俩都可以保证有序性啊?

首先,在讨论这个问题之前,我们要明确一个既定事实:

Java里只有 volatile 关键字是能实现禁止指令重排序的,起码在JDK1.8还是这样的

所以volatile可以保证有序性,那么synchronized又是如何保证有序性呢?

其实,这里的有序性对于它们两个的含义不太一样。

  • synchronized的有序性是指,被它修饰的方法或者代码块在任意时刻只能有一个线程执行,在块或者方法之间是有序的,有点类似宏观的有序。

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

synchronized是依赖于底层的操作系统的 Mutex Lock 来实现,在查看相应字节码时可以看到monitorentermonitorexit指令。

image.png

总结

总结一下,synchronized代码块与块之间是有序的,但是在代码块里面并不是原子操作,是可能会发生指令重排序的,而volatile关键字,基于它的实现原理,正好可以禁止指令重排序,所以在双重检查锁里面,需要有volatile关键字。