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的正确使用姿势相关推荐

  1. JAVA volatile关键字正确使用姿势

  2. 论机器学习的正确学习姿势

    论机器学习的正确学习姿势 策划 | 刘燕作者 | Caleb Kaiser翻译 | Sambodhi编辑 | Linda很多开发人员并没有机器学习领域的背景,在机器学习如火如荼的今天,没学过机器学习的 ...

  3. 什么叫取反_转载:CodeReview正确的姿势是什么?

    作者:微博是阿里孤尽 链接:https://www.zhihu.com/question/383079175/answer/1109655276 来源:知乎 著作权归作者所有.商业转载请联系作者获得授 ...

  4. class ts 扩展方法_ts类型声明文件的正确使用姿势

    ts类型声明文件的正确使用姿势 ts声明文件类型 npm install @types/jquery --save-dev 与npm一同发布 解释: package.json 中有 types 字段, ...

  5. java同步锁如何使用_java 同步锁(synchronized)的正确使用姿势

    关于线程安全,线程锁我们经常会用到,但你的使用姿势正确不,反正我用错了好长一段时间而不自知.所以有了这篇博客总结下线程锁的正确打开姿势 废话不说看例子 一,对整个方法进行加锁 1,对整个方法进行加锁, ...

  6. 直播预告|中台基石腾讯云TStack的正确使用姿势

    各位亲爱的小伙伴们,你们好啊~~ 他二哥技术直播又和大家见面了! 还记得在第一期的节目中,Sandy小姐姐和两位技术哥哥带我们见识了腾讯自研交换机的强大实力. (赶紧戳链接复习☝) 这次直播,他二哥邀 ...

  7. MongoDB系列:五、MongoDB Driver使用正确的姿势连接复制集

    MongoDB复制集(Replica Set)通过存储多份数据副本来保证数据的高可靠,通过自动的主备切换机制来保证服务的高可用.但需要注意的时,连接副本集的姿势如果不对,服务高可用将不复存在. 使用复 ...

  8. java 日志使用_Java日志正确使用姿势

    前言 关于日志,在大家的印象中都是比较简单的,只须引入了相关依赖包,剩下的事情就是在项目中"尽情"的打印我们需要的信息了.但是往往越简单的东西越容易让我们忽视,从而导致一些不该有的 ...

  9. Python re 库的正确使用姿势

    前提假设: 已经充分掌握 PCRE 风格正则表达式 熟读 re 库文档 Why 正则表达式的强大已不用我赘述,Python 对此的支持也是十分强大,只不过: re.search(pattern, st ...

最新文章

  1. 宏基因组实战4. 基因注释Prokka
  2. FFmpegInterop 库在 Windows 10 应用中的编译使用
  3. [Java开发之路]Java字符串
  4. python读取word中后缀名docx的文件的表格
  5. 【OpenCV归纳】4 关于HighGUI
  6. 操作系统内存管理--简单、页式、段式、段页式
  7. P2305 [NOI2014]购票
  8. [css] 写出几个初始化CSS的样式,并解释说明为什么要这样写
  9. 面试:一文搞懂堆和栈的区别
  10. FLEX:target和currentTarget属性的区别
  11. 算法——从旋转字符串到翻转单词
  12. 人脸验证(六)--SphereFace
  13. Linux之netstat命令详解
  14. Java 数据库image型输出图片
  15. FastDFS安装手册
  16. 如何恢复被删短信_手机短信删除了怎么恢复?超级简单的两种方法
  17. 随机生成中文姓名(分性别)
  18. Unity3d开发“类三消”游戏
  19. Unity 做一个提示框,背景图片随文字改变大小
  20. 手机配件市场上的“隐形巨头”:80后长沙夫妻创办,IPO首日市值逼近600亿

热门文章

  1. Grunt 入门指南3:创建Tasks
  2. eclipse使用方法02——链接Navigator view 与 Editor
  3. Python学习笔记之列表(五)
  4. Clover支持目录多标签页
  5. 【转载】Apache Ranger剖析:Hadoop生态圈的安全管家
  6. linux百度云备份文件夹,Linux环境下载百度网盘文件
  7. 关于LabVIEW视觉ROI的读取与存储
  8. 关于TestNg注解执行
  9. ios 各种锁的使用性能比较
  10. 图形用户界面和交互输入方法---图形用户界面的设计