volatile
作用主要有如下两个:
1.线程的可见性:当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。
2.顺序一致性:禁止指令重排序。
线程的可见性实现原理
线程之间的共享变量存储在主内存中,每个线程都一个都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本。所以正常情况下当一个线程把主内存中的共享变量读取到自己的本地内存中,然后做了更新。在还没有把共享变量刷新到主内存的时候,另外一个线程是看不到的。
而被volatile修饰的变量被修改时,会将修改后的变量直接写入主存中,并且将其他线程中该变量的缓存置为无效,从而让其它线程对该变量的引用直接从主存中获取数据,这样就保证了变量的可见性
通过demo来测试一下
首先是没有使用volatile关键字时,可以看到Thread2执行结束,count的值已经被修改,但是Thread1获取不到,只能在while循环中自旋。
当加上volatile关键字后,打印台展示了Thread1执行结束后的内容,说明Thread1获取到了Thread2对count的修改结果,使得while循环不成立,终止了自旋
demo代码:
volatile Integer count = 1;
public void updateCount() {
this.count = 2;
System.out.println("修改count值为:" + this.count);
}
public static void main(String[] args) {
FileUtils test = new FileUtils();
new Thread(() -> {
//如果未获取到count被修改,则一直执行while循环
// 如果获取到count的值被修改,则while不成立,执行打印语句
while (test.count==1) {
}
System.out.println("获取到count的最新值,线程"+Thread.currentThread().getName() + "结束");
}, "Thread1").start();
new Thread(() -> {
try {
Thread.sleep(5000);
test.updateCount();
} catch (InterruptedException e) {
}
}, "Thread2").start();
}
禁止指令重排
CPU 存在乱序执行的,但Volatile 可以保证禁止指令重排(乱序执行)
什么是指令重排:
java语言规范规定JVM线程内部维持顺序化语义。即只要程序的最终结果与它顺序化情况的结果相等,那么指令的执行顺序可以与代码顺序不一致,此过程叫指令的重排序。指令重排是处理器为提高运算速度而做出违背代码原有顺序的优化。指令重排可以保证串行语义一致,否则我们的应用程序根本无法正常工作,但是没有义务保证多线程间的语义也一致。除了使用 volatile 关键字外,我们还可以使用 synchronized 关键字来禁止指令重排。
为什么禁止指令重排序 :
假设volatile 没有指令重排功能 变量只会保证该方法执行过程中所有依赖值结果的地方都能获得到正确的结果 但不能保证该方法的执行顺序 与写入代码的顺序一样 这样在多线程环境下就会出现问题 禁止指令重排的主要目的是确保程序的正确性和可靠性。在并发编程中,指令重排可能会导致程序产生一些难以预料的错误结果,例如死锁、数据竞争等。这些问题不仅会直接影响程序的运行效率和稳定性,而且还可能会导致数据损失、安全漏洞等问题。
因此,禁止指令重排可以保证程序的正确性和可靠性。在多核 CPU 上,如果禁止指令重排,程序的执行顺序就会与代码的编写顺序一致,这将有助于减少并发编程中出现的各种问题,从而提高程序的性能和可靠性。
volatile总结:
1.保证可见性
2.禁止指令重排
上一篇 >>>>> Synchronized关键字的简单认识
That’s it;
欲买桂花同载酒,终不似,少年游。