Java中的关键字volatile解析
volatile是研究Java并发编程绕不过去的一个关键字,先说结论:
volatile的作用:
1.保证被修饰变量的可见性
2.保证程序一定程度上的有序性
3.不能保证原子性
下面,我们将从理论以及实际的案例来逐个解析上面的三个结论
一、可见性
什么是可见性?
举个例子,小明和小红去看电影,刚开始两个人都还没买电影票,小红就先去买了两张电影票,没有告诉小明。小明以为小红没买,所以也去买了两张电影票,因为他们只有两个人,所以他们只能用两张票,这就是小明和小红他俩电影票的数量的可见性。
在讲解之前,我们简单的了解一下JVM当中运行时数据区的结构
堆内存:存放的就是对象,所以它也是JVM当中内存最大的一区域
线程私有区:线程中的栈会去从堆当中获取变量的值来进行操作,正是因为是私有化的,所以两个线程之间的数据是不会共享的
元空间:存放静态变量以及常量还有被虚拟机加载的类信息
同理,我们可以将小明和小红看作java当中的两个线程1和2,共有一个变量
public class volatileTest {public static boolean flag = false;public static void main(String[] args) {try {new Thread(() -> {System.out.println("线程1开始");//线程1当中取反值,当flag为true时才会跳出循环while (!flag) {}System.out.println("线程1结束");}).start();Thread.sleep(100);new Thread(() -> {System.out.println("线程2开始");//线程2给flag赋值flag = true;System.out.println("线程2结束");}).start();} catch (Exception e) {e.printStackTrace();}}
}
该代码的运行结果如下:
可以很清楚的看到,只有线程2是跑完了的,但是明明线程2已经给flag赋值,线程1并没有停止循环,这就是flag这个变量没有可见性,导致线程1一直不停止
解决的方法有两种
第一:让每个线程空余时间就去堆同步数据(显然不合理)
第二:使用volatile关键字去修饰变量flag
让我们加上volatile试试:
这回线程1总算是成功停止了,由此我们可得,volatile是可以让变量具有可见性的。
学习编程不能只知道如何去使用,而是要知道原理,这样才会有更多的薪资
那么volatile的底层是如何实现的呢?
如上面jvm运行数据区的图所示,所有的变量都是存在了堆当中,而每个线程都是拿到他们的副本进行计算和修改,volatile干了啥事呢,如下图所示
这里我们介绍一个新的概念,叫总线(各位可以把它理解成进行连接线程和堆内存,在计算机的硬件当中,也是有总线的,了解的朋友可以把它用相同概念理解一下)。
当一个被volatile修饰的变量,在某一个线程当中被修改时,总线会监听到这个变动,并且会让其他线程中的这个变量失效,简而言之,当线程2当中堆flag进行了修改,则会导致线程1当中的flag失效,就是把这个线程1当中的flag删了。当线程1中没有flag了,它会重新去获取flag,这个时候,就会使我们的变量flag具有了可见性。
现在我们已经知道了,volatile的实行原理,那么它的底层是如何实现的?
众所周知,java语言加载时 -> class ->汇编语言 -> 机器语言,因为volatile是个关键字,所以它的底层是一种汇编语法,被volatile修饰的变量其实就是给它加了个一个lock前缀指令。
也就是说,当面试官问到我们,如何手写一个volatile时,我们可以说在编译的层面,添加一个lock前缀指令相当于一个内存屏障,它本身会提供三个功能
1)它会强制堆缓存的修改操作立即写入主存
2)如果是写操作,它会导致其他CPU中对应的缓存行无效
3)它会确保指令重排序时不会吧其它的指令排到内存屏障之前的位置,也不会之前的操作拍到内存屏障之后
前面两点很好理解,并且我们也进行了进一步的认证,第三点可能有朋友不太明白,这就引出了我们下一个论点,volatile可以保证一定的有序性
二、有序性
我们看下面三行代码
int i=1;
int j=2;
i =i++;
在我们的理解当中,程序时自上而下运行的,先是第一行,再是第二行等,然而事实上,jvm可能会对代码进行重排序,比如它可能就会让上面的这三行代码变成下面的状态
int i=1;
i =i++;
int j =2;
为什么会进行重排序,目的是让代码执行的速度更快,当然它也不是随便乱排的,排序的规则是根据代码的依赖性进行的判断,简而言之就是在不影响结果的情况下进行排序,感兴趣的朋友可以自行去了解一下
这是java本身对程序保证的有序性,在不影响运行结果的情况下进行重排序,但是仅限于单线程的情况下,在多线程的情况中,并不能有效地保证程序的有序性
下图为手写的一个单例模式,不做过多的赘述,左边为代码,右边为翻译的字节码文件
通过上图可以很清晰的看出,new OnlyObject这个操作重点分为了四步,
第一步:创建这个对象
第二步:调用这个类的构造方法
第三步:添加指向(就是从私有线程当中执行堆)
第四步:加载
由于java对程序的重排序,会使第二步和第三步进行调换位置,在单线程当中不会有任何问题,而在多线程当中就有问题了
看下图代码
当线程1已经完成添加指向时,在堆当中其实已经分配了一个值,但是这时并没有调用构造方法,所以导致此时这个对象只是一个半成品对象 ,里面并不是我们想要的值。这时线程2走进来,他发现object并不为空,所以直接返回了,此时的程序跟我们的业务并不相符,所以我们需要使用volatile来保证我们的有序性。
以上都是本人的拙见,有错误的地方还请大家帮忙指出,谢谢各位
Java中的关键字volatile解析相关推荐
- Java中的关键字--volatile
volatile关键字经常用来修饰变量.不过,volatile本身很容易被误用.本篇就介绍一下volatile的原理和使用方式. 在介绍volatile关键字原理前,我们首先要了解JVM运行时的内存分 ...
- java中static关键字的解析
静态的特点: A:随着类的加载而加载 B:优先于对象而存在 C:静态是被所有对象共享的数据 这也是我们来判断是否使用静态的标准 D:静态的出现,让我们的调用方式多了一种 类名.静态的内容 非静态的内容 ...
- Java中synchronized和volatile有什么区别?
Java中synchronized和volatile有什么区别? 相关内容 synchronized的问题 什么叫做不完整对象,这个怎么理解呢? 总结 ) 相关内容 1.Java语言为了解决并发编程中 ...
- java中的关键字static
原文链接: https://zhuanlan.zhihu.com/p/70110497 昨晚面试中被问到,没能回答出来.这篇答疑文章写得非常好,无一字可增删,遂直接复制. 在平时开发当中,我们经常会遇 ...
- java 死锁 内存消耗_详解Java中synchronized关键字的死锁和内存占用问题
先看一段synchronized 的详解: synchronized 是 java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并 ...
- [转载] Java中的关键字含义以及介绍
参考链接: C++和Java中static关键字的比较 Java关键字是有特别意义的标识符.用来表示一种数据类型,或者表示程序的结构等. Java关键字不能用作变量名.方法名.类名.包名和参数. 以下 ...
- java中的关键字英汉
java中的关键字 初入java记录贴 类: enum(枚举).interface(接口).class(类).extends(继承).implements(实现接口) 对象: new(创建).inst ...
- 【Java_基础】Java中Native关键字的作用
本篇博文转载与:Java中Native关键字的作用 转载于:https://www.cnblogs.com/leiblog/p/10529056.html
- java中final关键字的使用
final 中文翻译为 最终的,在java中也是较为常用的关键字之一. 在java 中 final 关键字可以修饰 类.方法.变量 final 修饰在类上,则表示该类不能被继承,如果里面的成员变量没 ...
最新文章
- linux python2和python3共存_linux-Centos7安装python3并与python2共存
- 两款接口测试工具推荐
- 存储的瓶颈--大型网站技术演进思考
- 大学学习方法介绍之:使用两套教材
- 新零售赛道上,便利蜂的美食牌
- 2009年5月软件设计师考前预测试题及考点解析
- [Leetcode][第1025题][JAVA][除数博弈][数学][递推]
- linux php ldap_linux php ldap安装配置的方法
- avs3 ts格式封装 标准_超能课堂(204):多媒体容器格式变迁录
- 用户权限管理shiro
- 【数据结构的魅力】004.堆、前缀树、桶排序、排序算法总结
- Elasticsearch6.3.0环境安装
- Sigmoid函数简介
- 《金字塔原理》要点汇总
- css在线代码生成工具汇总
- Raspberry 2B+系统安装Hass
- 优秀员工评审表 模板
- 使用原版镜像安装windows时install.wim文件过大的解决方案
- Hive之——数据定义
- java throw 什么意思_Java中throw和throws有什么区别?
热门文章
- python培训班靠谱吗-Python培训机构就业靠不靠谱?
- 语音识别及其定点DSP实现
- MongoDB创建集合及创建文档(非常详细哦~)
- 最简单的基于FFmpeg的libswscale的示例附件:测试图片生成工具
- ITU-T Technical Paper: NP, QoS 和 QoE的框架以及它们的区别
- bootsect.exe linux,bootsect.exe
- layui table reload post请求_基于Layui组件封装的后台模版
- python困境_学习 Python 编程的三种困境
- phpmyadmin/scripts/setup.php,Linux下phpMyAdmin安装过程中的问题解决
- 计算机上机单招试题及答案,全国高职单招计算机类模拟试卷考卷及答案