volatile的正确使用姿势
https://zhuanlan.zhihu.com/p/112742540
https://blog.csdn.net/l18848956739/article/details/105838177
volatile特性
volatile具备并发三大特性当中的两种:
- 可见性
简单地说就是volatile变量修改后,所有线程都能立即实时地看到它的最新值。
- 有序性
有序性是指系统在进行代码优化时,不能把在volatile变量操作后面的语句放到其前面执行,也不能将volatile变量操作前面的语句放在其后执行。
那接下来我们先来看下volatile关键字是如何解决多线程可见性问题的。
volatile可见性
下面的两个例子演示了变量使用volatile和未使用volatile时,变量更新对多线程执行的影响。
//未使用了volatile
public class NonVolatileDemo {public static boolean stop = false;//任务是否停止,普通变量public static void main(String[] args) throws Exception {Thread thread1 = new Thread(() -> {while (!stop) { //stop=false,不满足停止条件,继续执行//do someting}System.out.println("stop=true,满足停止条件。" +"停止时间:" + System.currentTimeMillis());});thread1.start();Thread.sleep(100);//保证主线程修改stop=true,在子线程启动后执行。stop = true; //trueSystem.out.println("主线程设置停止标识 stop=true。" +"设置时间:" + System.currentTimeMillis());}
}
NonVolatileDemo中,停止标识stop未使用volatile关键字修饰,初始值为false。
- 创建子线程thread1并启动,在子线程thread1任务中使用停滞标识stop作为判断条件:
- 当不满足停止条件时,线程会一直运行;
- 当满足停止条件,终止任务。
- 稍后,我们在主线程中设置停止标识为true。
我们希望在设置stop=true之后,子线程能够获得到判断条件的变化而停下来
但是执行代码,结果如下图,我们可以看到在主线程设置stop=true后,子线程未及时感知到stop的变化,还在继续执行任务。
也就是子线程并没有知道stop的值改变的这件事情,why???来看看图解:
大家如果对于read/use等指令是干什么的不了解的也不用担心,这是JVM的原子性指令,跟volatile如何实现有序性的内存屏障相关,我们会在下一篇文章中详细讲解。
那通过上图大家知道了一个普通变量在多线程环境下的运行机制,所以为什么出现这个现象也就清楚了,那我们怎样能够解决这个问题呢?使用volatile修饰我们的变量,让它对于所有线程都具有可见性,来看下面的代码
//使用了volatile
public class VolatileDemo {public static volatile boolean stop = false;//任务是否停止,volatile变量public static void main(String[] args) throws Exception {Thread thread1 = new Thread(() -> {while (!stop) { //stop=false,不满足停止条件,继续执行//do someting}System.out.println("stop=true,满足停止条件。" +"停止时间:" + System.currentTimeMillis());});thread1.start();Thread.sleep(100);//保证主线程修改stop=true,在子线程启动后执行。stop = true; //trueSystem.out.println("主线程设置停止标识 stop=true。" +"设置时间:" + System.currentTimeMillis());}
}
在VolatileDemo中,停止标识stop使用volatile关键字修饰,初始值为false。其他代码和NonVolatileDemo完全一致。
结果出来了,NonVolatileDemo代码就存在可见性问题;而在VolatileDemo中通过使用volatile关键字,很简单的保证了多线程下共享变量的可见性。
好奇volatile怎么实现的吗?来来来一张图弄懂它!
现在是不是一目了然了呢?不过大家要注意一点,为了让大家好理解我引入了工作内存的概念,但是实际上工作内存不是真实存在的,想要了解真实的实现逻辑需要了解更多偏硬件的知识,大家有兴趣了解可以关注公众号,回复关键字"工作内存”来领取相关资料。
而且可见性的实现还有更深层的原理,需要配合有序性和内存屏障才能讲透彻,所以有兴趣的同学记得关注下一篇原理剖析哦
初步了解了volatile的基本使用及其可见性,下面我们来看看volatile和synchronized的区别
先上段代码,使用synchonized实现VolatileDemo功能,来直观感受下。
public class SychronizedDemo {public static boolean stop = false;//任务是否停止//同步静态方法,设置stoppublic static synchronized void setStop(boolean flag) {SychronizedDemo.stop = flag;}//同步静态方法,获取stoppublic static synchronized boolean isStop() {return stop;}public static void main(String[] args) throws Exception {Thread thread1 = new Thread(() -> {while (!isStop()) { //stop=false,不满足停止条件,继续执行//do someting}System.out.println("stop=true,满足停止条件。" +"停止时间:" + System.currentTimeMillis());});thread1.start();Thread.sleep(100);//保证主线程修改stop=true,在子线程启动后执行。setStop(true); //trueSystem.out.println("主线程设置停止标识 stop=true。" +"设置时间:" + System.currentTimeMillis());}
}
在SychronizedDemo中,停止标识stop为普通静态变量,初始值为false。stop的设置方法setStop或获取方法isStop都为同步方法,可以保证锁对象SychronizedDemo类静态变量stop的可见性。执行代码,结果如下图,我们可以看到在主线程设置stop=true后,子线程同时感知到stop的变化终止了任务。
大家可以看到volatile和synchronized都实现了同样的功能,但底层实现是有一定差异的。
volatile关键字是Java提供的最轻量级的同步机制,为字段的访问提供了一种免锁机制,使用它不会引起线程的切换及调度。这时使用volatile要比synchronized要简单有效的多,如果使用synchronized还会影响系统的吞吐量。
那volatile关键字即可以保证可见性,而且使用起来这么方便,那它是解决可见性的万能药吗?我能用volatile代替synchonized吗?
事实上,volatile关键字并不是万能的,因为我们前文讲到了volatile并不能保证原子性。
volatile的正确使用方式
使用 volatile 变量的主要原因是单个字段同步操作的简易性。如果只使用了volatile就能实现线程安全,那就放心的使用它,如果同时还需要添加其他的同步措施,那就不要使用。
正确使用的场景举例:变量本身标识是一种状态,或者引用变量的某些属性状态,在代码中需要确保这些状态的可见性,这时就可使用volatile。volatile 变量仅仅是一个状态标识,用于指示发生了一个重要的一次性事件,例如完成初始化标识或请求终止标识。
volatile boolean stop=false;//volatile 变量,用于停止请求的状态标识
public void shutdown() {//停止请求stop = true;
}
public void doWork() {while (!stop) {//判断是否需要停止// do Something }
}
这样只要任何一个线程调用了shutdown(),其他线程在执行doWork时都可以立即感知到stop变量的变化,这时就可以大胆的使用volatile。这种类型的状态标记的一个公共特性是:通常只有一种状态转换,如标志从false 转换为true。
volatile的正确使用姿势相关推荐
- JAVA volatile关键字正确使用姿势
- 论机器学习的正确学习姿势
论机器学习的正确学习姿势 策划 | 刘燕作者 | Caleb Kaiser翻译 | Sambodhi编辑 | Linda很多开发人员并没有机器学习领域的背景,在机器学习如火如荼的今天,没学过机器学习的 ...
- 什么叫取反_转载:CodeReview正确的姿势是什么?
作者:微博是阿里孤尽 链接:https://www.zhihu.com/question/383079175/answer/1109655276 来源:知乎 著作权归作者所有.商业转载请联系作者获得授 ...
- class ts 扩展方法_ts类型声明文件的正确使用姿势
ts类型声明文件的正确使用姿势 ts声明文件类型 npm install @types/jquery --save-dev 与npm一同发布 解释: package.json 中有 types 字段, ...
- java同步锁如何使用_java 同步锁(synchronized)的正确使用姿势
关于线程安全,线程锁我们经常会用到,但你的使用姿势正确不,反正我用错了好长一段时间而不自知.所以有了这篇博客总结下线程锁的正确打开姿势 废话不说看例子 一,对整个方法进行加锁 1,对整个方法进行加锁, ...
- 直播预告|中台基石腾讯云TStack的正确使用姿势
各位亲爱的小伙伴们,你们好啊~~ 他二哥技术直播又和大家见面了! 还记得在第一期的节目中,Sandy小姐姐和两位技术哥哥带我们见识了腾讯自研交换机的强大实力. (赶紧戳链接复习☝) 这次直播,他二哥邀 ...
- MongoDB系列:五、MongoDB Driver使用正确的姿势连接复制集
MongoDB复制集(Replica Set)通过存储多份数据副本来保证数据的高可靠,通过自动的主备切换机制来保证服务的高可用.但需要注意的时,连接副本集的姿势如果不对,服务高可用将不复存在. 使用复 ...
- java 日志使用_Java日志正确使用姿势
前言 关于日志,在大家的印象中都是比较简单的,只须引入了相关依赖包,剩下的事情就是在项目中"尽情"的打印我们需要的信息了.但是往往越简单的东西越容易让我们忽视,从而导致一些不该有的 ...
- Python re 库的正确使用姿势
前提假设: 已经充分掌握 PCRE 风格正则表达式 熟读 re 库文档 Why 正则表达式的强大已不用我赘述,Python 对此的支持也是十分强大,只不过: re.search(pattern, st ...
最新文章
- 宏基因组实战4. 基因注释Prokka
- FFmpegInterop 库在 Windows 10 应用中的编译使用
- [Java开发之路]Java字符串
- python读取word中后缀名docx的文件的表格
- 【OpenCV归纳】4 关于HighGUI
- 操作系统内存管理--简单、页式、段式、段页式
- P2305 [NOI2014]购票
- [css] 写出几个初始化CSS的样式,并解释说明为什么要这样写
- 面试:一文搞懂堆和栈的区别
- FLEX:target和currentTarget属性的区别
- 算法——从旋转字符串到翻转单词
- 人脸验证(六)--SphereFace
- Linux之netstat命令详解
- Java 数据库image型输出图片
- FastDFS安装手册
- 如何恢复被删短信_手机短信删除了怎么恢复?超级简单的两种方法
- 随机生成中文姓名(分性别)
- Unity3d开发“类三消”游戏
- Unity 做一个提示框,背景图片随文字改变大小
- 手机配件市场上的“隐形巨头”:80后长沙夫妻创办,IPO首日市值逼近600亿