Java-volatile是如何实现的
如果下面两段代码你能清楚的知道是为什么,那么就没有必要看本文了
代码示例1:下面的代码不会打印出“粮粮”
public class Act {static int a = 0;public static void main(String[] args) throws InterruptedException {new Thread(() -> {Thread.sleep(1000);// 省略try catcha = 1;}).start();while (a == 0) {}System.out.println("粮粮");}
}
代码示例2:下面的代码会打印出“粮粮”
public class Act {static int a = 0;public static void main(String[] args) throws InterruptedException {new Thread(() -> {Thread.sleep(1000);// 省略try catcha = 1;}).start();while (a == 0) {//同示例1相比,只多出了这一行代码System.out.println();}System.out.println("粮粮");}
}
本文对于C语言的朋友来说可能比较容易阅读,但是对于只会Java的朋友来说稍微有些复杂,这篇AT&T拓展内联汇编(ATT/GCC)会让你更好的理解本文,当然了,不看也可以。。。
Java中volatile关键字的的作用是每次都能获取最新的变量(主存)数据,很明显,虽然上述2个示例中我没有使用volatile关键字,但是在示例2中,也依然达到了volatile的效果,本文要论述的问题就是示例2的底层本质,虽然本文标题是volatile如何实现,但文章内容更倾向于内存屏障,这也包含了volatile的在hotspot中的实现原理,所以我将本文标题定义为volatile实现
Load与Store
Load:将内存中的数据放到寄存器
Store:将寄存器中的数据放到内存
由于JVM是基于栈的虚拟机,所以JVM会使用栈来模拟寄存器,在java中叫做操作数栈,用栈帧中的局部变量表来模拟物理机器的内存,所以我们也可以说
Load操作 :从一个对象的成员变量中读取值
Store操作:往一个对象的成员变量中写入值
下面的代码解释了什么是Load和Store
public class Demo1{int a;//成员变量public void m1() {int p = a;//Load aa = 666; //Store a}
}
编译器重排序
下面的示例方法m1中,对于编译器来说,先执行p1 = a,还是先执行p2 = b,在单线程下语义都是一样的,所以编译器编译出来的文件有可能先执行p2 = b,再执行p1 = a,这种现象就叫做编译器重排序
代码示例3
public class Demo1{int a;//成员变量int b;//成员变量public void m1() {int p1 = a;//Load a操作int p2 = b;//Load b操作}
}
CPU重排序
CPU为了流水线饱和,很有可能同编译器一样也会进行重排序的操作,也就是说,对于上述代码,即使编译器不重排序,CPU也有可能进行重排序
内存屏障
那么如何禁止编译器重排序呢?使用一个叫做内存屏障的东西,内存屏障就像一堵墙,屏障两边的代码可以互相排序,但是屏障一侧的代码,不能排序到另一侧,其实本应该分两种情况来说,1种是编译器屏障,在hotspot中使用AT&T语法的拓展内联汇编的volatile关键字来实现,另1种是内存屏障,在hotspot中的拓展内联汇编中加入lock前缀,这两种看不明白没关系,总之记住一句话,能防止两部分代码重排序的方式,就叫做内存屏障,hotspot抽象出了4种屏障,请看下面代码中的注释部分
public class Demo1{int a;int b;public void m1() {int p1 = a;//Load a操作// 如果我在此处加一行代码,能导致先执行a,再执行b,那么这行代码就叫做内存屏障int p2 = b;//Load b操作}
}
LoadLoad屏障
上述代码如果在p1 = a和p2 = b之间加点东西,那么此时这个东西就叫做内存屏障,由于p1 = a和p2 = b都是Load操作,所以这种屏障就叫做LoadLoad屏障,下面的代码是稍微修改之后的代码
public class Demo1{int a;int b;public void m1() {int p1 = a;//Load a操作如果在此行插入屏障,则该屏障叫做LoadLoad屏障int p2 = b;//Load b操作}
}
如果想要禁止重排序,得加上一个LoadLoad屏障,那么具体如何加呢??在Java语言的层次上,是Unsafe类的fullFence,当然了,程序员无法直接使用该类,而且该类在java9中被其他类替代了,具体是什么我也没研究,总之,能明白这个意思就行,因为对于上述示例来说,真的没有必要禁止重排序,内存屏障主要的使用者是编译器,而不是程序员,后文会给出解释和示例
LoadStore屏障
同理,除了LoadLoad,还有LoadStore,StoreStore,StoreLoad屏障,下面的代码如果想要禁止重排序,则需要添加LoadStore屏障
public class Demo1{int a;int b;public void m1() {int p1 = a;//Load a操作如果在此行插入屏障,则该屏障叫做LoadStore屏障b=666; //Store b操作}
}
使用屏障的场景
到目前为止,上文虽然表达了什么是屏障,但是只是为了更好的解释什么是内存屏障,并不是真正的使用场景,因为上述代码即使不使用屏障也没有丝毫问题,那么在java中,到底满足哪些条件,才会使用屏障呢?在hotspot中,如果两个挨着的操作,满足如下关系,则会被自动插入内存屏障
根据上表规则可知,如下代码在编译时必定会被插入内存屏障
public class Demo{volatile int v1;int a;public void m1() {int p1 = v1;//Load v1,这个操作也叫读取volatile成员v1这里会被插入LoadStore内存屏障a=666; //Store a操作,这个操作也叫写入普通成员a}
}
到此为止,这也真正说明了为什么会说volatile有禁止重排序的效果,其实它的本质是编译器根据自己的规则,而这个规则波及到了被volatile修饰的成员变量,所以有人说volatile会禁止重排序,通过上述规则,那么我们可以大胆的推测出下面的代码也会被插入内存屏障
public class Demo{volatile int v1;int a;public void m1() {synchronized (this) {// 进入synchronized(线程进入监视区)这里会被插入LoadStore内存屏障 a = 666; // Store a操作,也叫写入普通成员a} // 退出synchronized(线程退出监视区)这里会被插入StoreLoad内存屏障 a = v1; // Load v1操作,也叫读取volatile成员v1}
}
内存屏障如何实现的?
4中内存屏障,LoadLoad,LoadStore,StoreStore,StoreLoad的实现方式是相同的,它们的源代码是GCC的AT&T内联汇编写的,定义如下
asm volatile ("":::"memory");
没错,如你所想,这行代码就是内存屏障,重点在于它不仅有内存屏障的功能,它还有清空寄存器的语义,"memory"表示每次读取都从主存(而非寄存器)读取数据,所以有内存屏障的地方,读取的数据都是最新的数据,回想本文开头的代码示例1和代码示例2,发现示例2会有volatile的效果,最终答案就是System.out.println方法里面有synchronized块,所以会出现内存屏障,而出现内存屏障,就会从主存读取数据,从而达到获取最新值的效果
Java-volatile是如何实现的相关推荐
- Java volatile关键字原理解剖
Java volatile关键字原理解剖 文章目录 Java volatile关键字原理解剖 参考文章 前置知识 CPU缓存模型 CPU缓存行 并发编程基本概念 Java锁概念 volatile关键字 ...
- [Java并发编程(三)] Java volatile 关键字介绍
[Java并发编程(三)] Java volatile 关键字介绍 摘要 Java volatile 关键字是用来标记 Java 变量,并表示变量 "存储于主内存中" .更准确的说 ...
- Java Volatile关键字可见性问题分析
Java Volatile关键字可见性问题分析 Java 内容模型 普通变量(非Vola变量)的内存不可见性 Volatile变量的内存可见性 剩余疑惑 Java 内容模型 具体可以查看这篇文章Jav ...
- volite java_如何理解 JAVA volatile 关键字
最近在重新梳理多线程,同步相关的知识点.关于 volatile 关键字阅读了好多博客文章,发现质量高适合小白的不多,最终找到一篇英文的非常通俗易懂.所以学习过程中顺手翻译下来,一方面巩固知识,一方面希 ...
- Java Volatile 详解
Java Volatile 详解 Volatile:是java虚拟机提供的轻量级的同步机制.保证可见性.禁止指令重排序.不保证原子性!!! 学习Volatile之前必须了解JAVA内存模型. Java ...
- java volatile原理A CUP层面
作者:知乎用户 链接:https://www.zhihu.com/question/49656589/answer/117826278 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业 ...
- Java Volatile keyword
java的volatile是什么意思 我们知道,在Java中设置变量值的操作,除了long和double类型的变量外都是原子操作,也就是说,对于变量值的简单读写操作没有必要进行同步. 这在JVM 1. ...
- 一个具体的例子学习Java volatile关键字
相信大多数Java程序员都学习过volatile这个关键字的用法.百度百科上对volatile的定义: volatile是一个类型修饰符(type specifier),被设计用来修饰被不同线程访问和 ...
- java volatile 用法_java关键字volatile用法详解
volatile关键字想必大家都不陌生,在java 5之前有着挺大的争议,在java 5之后才逐渐被大家接受,同时作为java的关键字之一,其作用自然是不可小觑的,要知道它是java.util.con ...
- java volatile 多线程_Java多线程之volatile
在学习Volatile之前有必要简单了解一下物理内存模型和Java的内存模型,这样对理解Volatile大有好处. 寄存器 首先我们要知道的是所有运算操作都是在CPU的寄存器中进行的,而CUP的执行涉 ...
最新文章
- 当下火热的大数据视频,免费送(含源码)
- 一、预备知识―程序的内存分配
- svn trunk branches tags 的用法
- js获取节点的DOM操作
- 吴恩达深度学习 —— 2.4 梯度下降
- 希尔排序的详细过程_算法系列: 10大常见排序算法(4)希尔排序
- Target-Action回调模式
- MPEG-7实例入门
- 基于react做了一个仿qq空间
- 现在被apihook搞郁闷了.....进展很慢...先放上几个必用的api说明吧.
- python程序题求roc-auc是一种常用的模型评价指标_【Python机器学习 5-3】模型评价指标及模型选择...
- kali使用jd-gui
- Java web speach api_HTML5 Web Speech API,让网站更有趣
- oracle数据库在mybatis中的数值类型(NUMBER型)
- 我们真的需要一部《数据安全法》
- HTML我的家乡宁夏学生网页设计作品 dreamweaver作业静态HTML网页设计模板 宁夏旅游景点网页作业制作...
- 手机搜狐概念版 html,搜狐领跑四大门户 首推H5技术手机概念版
- Oracle EBS 动态调用 XML Publisher 模板 输出不同的报表
- 游戏服务器 协议 安全问题,游戏服务器开发安全问题
- Java IO流之装饰模式与适配器模式讲解
热门文章
- MySQL解析json字符串的相关问题
- oracle 多版本技术,读书笔记: 关于oracle中多版本的问题
- php-fpm启动条件,php-fpm的启动、重启
- IDEA2018部署jeesite3完美运行教程
- The driver is automatically registered via the SPI and manual loading of the
- 获取和使用某些网站的iconfont图标字体
- 微服务的通信协议:Restful,RPC(Dubbo、Motan、gRPC)
- java十进制转换成二进制数
- linux文件替换命令sed使用
- SQL Server监控全解析