java:volatile关键字原理

volatile说明

在变量中声明后,能够在所有线程中共享改变量。并且volatile关键字能防止指令重排,即程序读取到volatile时,则不会将程序执行顺序修改。

 

先了解下内存模型

cpu内存模型

多核cpu在处理数据时,会通过系统总线把主内存中的数据读取副本到高速缓存中的缓存行,当其中一个cpu修改了当前缓存行的数据,会有两种方式保证数据的一致性

1、总线锁:因为高速缓存交互主存是需要通过系统总线的,所以修改后会将总线锁定,阻塞其它CPU访问主存,等当前CPU缓存写入到主存后释放锁。其它CPU才能从主存中读取数据。这种方式效率极低,影响其它CPU访问内存。

2、缓存一致性(MESI):当前CPU缓存行数据修改,通过总线将修改状态广播给其它CPU,让各个CPU根据状态判断本地情况进行响应,是失效还是重新读

 

java内存模型

        JVM内存结构,把内存分为 堆、栈、方法区、本地方法栈和程序计数器。其中堆和方法区被所有线程共享,在内存模型中被成为主内存,而栈、程序计数器属于线程私有,在内存模型中被成为工作内存。

  Java内存模型规定所有的变量都是存在主存当中(类似于前面说的物理内存),每个线程都有自己的工作内存(类似于前面的高速缓存)。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。

volatile作用

java中内存模型没有像CPU一样的总线来保证线程数据共享。这时候就需要volatile,volatile会强制将每个线程工作内存的变量修改主动的保存主存中。要读取时再从主存中读取

 

使用volatile达到的线程间变量共享的问题。但是无法保证数据原子性

因为多个线程同时执行,读取赋值写入,那么volatile修饰的变量则会已程序执行时的最后一个线程修改的参数为准。

 

保障volatile原子性

保障原子性的方式有三种synchronized、lock、atomic

两者是锁住当前变量的操作线程。

atomic是java提供的CAS来实现原子性操作

 

应用场景

需要通过某个状态控制调用volatile变量的线程(只有一个线程写,其它线程读)

 

与static区别

相同点:

1、都是为了保证线程共享

2、非原子性(操作变量)

不同点:

volatile唯一的好处就是多线程可以及时获取到最新的主存数据,这点跟static有点区别。static在多线程状态下可能获取不到最新数据。

JVM规范定义了线程对内存间交互操作:
Lock(锁定):作用于主内存中的变量,把一个变量标识为一条线程独占的状态。
Read(读取):作用于主内存中的变量,把一个变量的值从主内存传输到线程的工作内存中。
Load(加载):作用于工作内存中的变量,把read操作从主内存中得到的变量的值放入工作内存的变量副本中。
Use(使用):作用于工作内存中的变量,把工作内存中一个变量的值传递给执行引擎。
Assign(赋值):作用于工作内存中的变量,把一个从执行引擎接收到的值赋值给工作内存中的变量。
Store(存储):作用于工作内存中的变量,把工作内存中的一个变量的值传送到主内存中。
Write(写入):作用于主内存中的变量,把store操作从工作内存中得到的变量的值放入主内存的变量中。
Unlock(解锁):作用于主内存中的变量,把一个处于锁定状态的变量释放出来,之后可被其它线程锁定。

上面一段,只要知道,工作内存中保留了一份主存中的变量副本,因此写操作未必能马上更新到主存,读操作也未必能马上读取到主存中更新后的值,这与cpu时间片有关,能不能读取最新的值看缘分。


参考:

static和volatile的原理及区别:https://blog.csdn.net/x18094/article/details/108429511

Java并发编程:volatile关键字解析:https://www.cnblogs.com/dolphin0520/p/3920373.html

存储器 - 缓存一致性 MESI 协议:如何让多核CPU的高速缓存保持一致:https://www.cnblogs.com/binarylei/p/12590759.html

 

posted @ 2021-06-27 13:31  带着梦想的咸鱼  阅读(196)  评论(0编辑  收藏  举报