欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

volatile

程序员文章站 2021-12-29 11:53:01
请滑走,自用,无营养警告⚠特性 ,作用:保证可见性和顺序性。每个线程有自己私有的工作内存,对变量进行操作时,会从公有的主内存拷贝变量副本到工作内存,如果对声明了volatile的变量进行写操作,虚拟机就会向处理器发送一条lock前缀的指令,将新的数据写回到系统内存,并且使其它CPU中这个变量的缓存行无效。其他cpu再读这个变量时,发现自己的缓存失效了,就会从主内存读。修改后立即写入主内存,读取时立即从主内存读,保证缓存一致性。(相同数据,不同缓存中呈现着不同的表现)总线嗅探,通过嗅探在总线....

目录

读volatile修饰的变量     getstatic

写volatile修饰的变量  使用putstatic

volatile是如何保证有序性的:禁止指令重排

指令重排:

屏障:禁止指令重排

happens-before: 

as-if-serial 语义:


cas:乐观锁,假设没有冲突去尝试,直到成功

synchronized :悲观锁,非公平锁(随机唤醒线程,或者唤醒全部),有锁的升级

lock : api层面的锁

 

volatile

jvm 内存模型,实际上就是操作系统的一个内存池

volatile

读volatile修饰的变量     getstatic

每次读volatile修饰的共享变量都会从主内存中读取,然后再工作内存中生成一个副本,

变量存在一个栈空间中。

volatile

 

写volatile修饰的变量  使用putstatic

内存地址中加的锁是 cas 锁,

虚拟机栈中对变量进行修改,然后在主内存地址中对该变量的地址加锁,变成线程独占,

将修改后的值,写入到主内存中,解锁,实现了内存可见性。

内存可见性:内存模型volatile 是基于 Memory Barrier(内存屏障)实现的,jvm 在写入由volatile 修饰的变量后会插进一个Write-Barrier指令,在读这个字段之前插入一个Read-Barrier指令,就可以保证线程a写入变量后,其他线程访问该变量拿到的都是最新值,在写入a 之前的的写操作(前提是a已经写入成功),对其他线程也是可见的。因为 Memory Barrier 会刷出 cache 中所有先前的写入。

volatile

jvm 中虚拟机栈:一个线程一个虚拟机栈,随线程的创建而创建,随线程的消失而消失

每个虚拟栈中有多少个栈帧:每调用一次方法就会产生一个栈帧

 

volatile

jvm主内存:堆区+方法区

jvm工作内存:虚拟机栈

 

volatile是如何保证有序性的:禁止指令重排

禁止指令重排的过程:

需要使用一个插件才能更加清晰的看到不同,安装 jclasslib 插件,重启idea

volatile

volatile

使用 -p -v -l 参数,进行反编译,通过-v 参数输出了反编译的附加信息

volatilevolatile

volatile

有 volatile   

volatile

没有volatile, 

volatile

通过对class 文件进行反编译:生成 上面 Code 代码块的内容

可以看到不管是否是带有 volatile 生成的汇编内容是一样的。

之所以能识别是带有volatile,是由于jvm 会读取 Access flags 的内容

通过 cache->is_volatile() 进行判断是否含有volatile

 

指令重排:

1.编译期指令重排  jvm对代码进行编译优化,会发生指令重排

2.运行期指令重排 (cpu乱序执行带来的):JVM执行的时候,对有依赖性的语句,不会进行重排,对没有依赖性的指令都有可能会发生重排。

 

屏障:禁止指令重排

1.编译期,编译屏障

2.运行期,内存屏障

volatile

为什么有内存屏障:

因为CPU写内存机制有两种,
1. write through 异步写 

                    ①CPU将写请求写入 store buffer 中

                    ②CPU空闲的时间将写的数据输入内存(等待空闲的时候出现延迟)

2. write back 同步写

                   ① cpu将写请求写入 store buffer

                   ② 将数据输入内存,直接输入

当有了内存屏障的,在使用异步的方式,写入store buffer中后,将cpu总线上锁,此时CPU 就空闲了,将数据写入内存,这样就达到了同步写的效果。保证了读写的有序性(只有cpu总线解锁了才能进行读操作)。

 

happens-before: 

定义happends-before 规则,就是为了不让jvm对代码进行编译优化,保证并发编程的正确性

规则为:

1.程序的顺序性 ,手动对程序推演的结果一定==编译器最后执行的结果,但是编译器不一定按照推演的顺序编译

2.volatile,对 volatile 变量的写操作 在 读操作之前

3.传递性,a 在b 之前,b在c之前,a一定在c之前

4.锁规则,先解锁,再加锁

5.线程start()规则,线程a 调用线程b,则 b可以看到在调用b之前的操作

6.线程join()规则,线程b 加入到线程 a, 线程b执行完后,线程a 可以看到线程b 的所有操作。

 

as-if-serial 语义:

编译器、runtime和处理器都必须遵守as-if-serial语义,不会对存在数据依赖关系的操作做重排序

 

 

 

本文地址:https://blog.csdn.net/yang_zzu/article/details/107675829