java工程师成神之路基础篇说volatile 可以禁止指令重排,但是又说只能修饰变量,不能修饰代码块,那怎么做到禁止指令重排的呢?怎么理解?
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
在Java中,volatile
关键字确实主要用于变量,不能直接用于修饰代码块。它主要提供两个关键特性:可见性和禁止指令重排。理解其如何禁止指令重排,需要深入到Java内存模型(JMM, Java Memory Model)的层面。
首先,volatile
确保了变量的修改对所有线程是立即可见的,即当一个线程修改了被volatile
修饰的变量时,其他线程可以立即看到这个变化,而不需要通过主内存来同步。
关于禁止指令重排,这里的关键在于volatile
变量的写操作和读操作具有所谓的“内存屏障”效果。内存屏障是一种抽象的概念,实际上是一组CPU指令,用于控制内存访问的顺序。当编译器或处理器在生成执行代码时,会遵循这些规则来避免某些类型的指令重排。
volatile
变量之前插入。它确保在该变量写入前,所有对内存的修改都已完成,并且这些修改对其他CPU是可见的。volatile
变量之后插入。它确保在此变量的值被读取后,所有后续的读取或写入操作都不会被提前执行。通过这两个屏障,volatile
变量的操作与其他非volatile
变量的操作之间形成了某种顺序关系,从而间接地影响了整体的指令执行顺序,有效地防止了可能导致程序逻辑错误的特定类型指令重排。
假设有一个场景,我们需要确保某个状态标志(flag)被正确设置,并且相关的初始化操作已经完成,才能让其他线程继续执行。如果使用volatile
修饰这个标志,那么在设置这个标志之前的所有初始化操作都不会被重排到设置标志之后,保证了其他线程看到这个标志时,相关初始化已完成。
volatile boolean flag = false;
void init() {
// 初始化操作...
data = new Data(); // 假设这是复杂的初始化过程
flag = true; // 设置标志
}
在这个例子中,由于flag
是volatile
的,所以任何线程看到flag
为true
时,都能确保data
已经被正确初始化了,即使是在多线程环境下。
总之,虽然volatile
不能直接作用于代码块以禁止指令重排,但它通过对变量读写操作的特殊处理,间接实现了对涉及该变量的指令序列的排序限制,从而保障了程序的正确性。