voliate关键字的应用误区

voliate关键字的应用误区写下这篇博客也是因为本人之前对voliate关键字理解不透彻,才有了应用误区,希望同样没有理解到位的朋友可以一起踩坑,也欢迎上帝视角明明白白的大佬指出本文的不当之处。先说一下自己之前对voliate的理解,voliate通过内存屏障可以禁止指令重排序和保证可见性,但是不能保证并发安全。禁止指令重排序就不说了,主要说一说如何保证可见性以及为什么不能保证并发安全。首先理解一下一个线程如何去修改…

大家好,又见面了,我是你们的朋友全栈君。

写下这篇博客也是因为本人之前对voliate关键字理解不透彻,才有了应用误区,希望同样没有理解到位的朋友可以一起踩坑,也欢迎上帝视角明明白白的大佬指出本文的不当之处。

先说一下自己之前对voliate的理解,voliate通过内存屏障可以禁止指令重排序和保证可见性,但是不能保证并发安全。禁止指令重排序就不说了,主要说一说如何保证可见性以及为什么不能保证并发安全。

首先理解一下一个线程如何去修改变量a的值,有三步:

第一步从主内存中读取a的值到该线程的工作内存。

第二步在工作内存中修改a。

第三步将a的值刷回主内存。

被voliate修饰的变量在进行写操作时,处理器会多一条Lock指令,Lock指令会将修改后的值直接写进主内存。同时还会让其他线程的工作内存中缓存的a的值失效,这两点就可以保证了可见性,我理解的可见性是指所有线程修改a之前a的值都和主内存中一样,都是一致的。

举个栗子如果有两个thread同时操作voliate修饰变量a,thread1和thread2都将a的值从主内存中读取到工作内存中之后,thread2对a做了修改之后会使thread1工作内存中a的值失效,同时及时地将a的值刷新进主内存,这时候thread1想去修改a发现自己工作内存中的值失效了,就会去主内存中重新读取,这样就避免了典型的并发问题。日常voliate的应用场景中喜欢用来修饰全局变量,曾经以为这样就可以避免该全局变量并发问题,其实不是的。

因为voliate不能将一个非原子性的操作变为原子性。所谓原子性的操作是指该操作在执行期间不受其他操作影响。上面这个对例子中对变量a的值做修改这就不是一个原子性操作,因为这个操作有三步。

还是刚刚那个例子加深一下理解。thread1如果在第一步和第二步中间a的值被thread2修改了,这时候thread1应该在thread2修改之后的a的基础上再做修改,总之thread1的操作结果就要受其他线程影响了。重点来了!voliate可以保证thread1的操作结果正确,但是它不能阻止thread2去影响thread1的操作结果。这就是voliate不能将一个非原子性的操作变为原子性,但是加锁(该变量只能同时被一个线程操作)或者CAS无锁可以,这是题外话。

说了这么多还是没有说为什么voliate不能保证并发安全。先看个情景,还是上面这个例子,刚说thread1在第一步和第二步之间的时候thread2把a修改了,这时候voliate棒棒的一系列骚操作让thread1在执行第二步真正去修改a的时候拿到的还是最新的即thread2修改之后刷新回主内存的值。但是如果thread1在第二步和第三步之间的时候thread2把a修改了呢!这时候thread1已经把a修改完了,已经准备刷回主内存了,这里大家不要和我一样钻牛角尖,之前我也在想,thread2把a修改了刷回主内存那thread1工作内存里a的值不就失效了?thread1已经修改完了就不会再去主内存中读a的值再修改一遍了,它现在要做的是把刚刚修改好a的值也刷回主内存,哦吼,这样一刷就出问题了。这就是voliate不能保证原子性也就是为什么不能保证并发安全的原因。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/143940.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 什么是通配符掩码

    什么是通配符掩码通配符掩码(WildcardMask)—通配符掩码(WildcardMask)是一个32位的数量,用在与一个IP地址的联合上来决定在一个IP地址的那个位应该不忽略,在将那个地址与另一个IP地址相比时。一个通配符掩码在设置接入列表时被指定。路由器使用的通配符掩码(或者称作反掩码)与源或目标地址一起来分辨匹配的地址范围,它跟子网掩码刚好相反。它像子网掩码告…

    2022年7月24日
    7
  • 文件 与 链表的同步

    文件 与 链表的同步

    2021年8月10日
    51
  • Idea激活码最新教程2024.2.1版本,永久有效激活码,亲测可用,记得收藏

    Idea激活码最新教程2024.2.1版本,永久有效激活码,亲测可用,记得收藏Idea 激活码教程永久有效 2024 2 1 激活码教程 Windows 版永久激活 持续更新 Idea 激活码 2024 2 1 成功激活

    2025年5月28日
    3
  • IOC控制反转的三种方式

    IOC控制反转的三种方式这里写自定义目录标题IOC控制反转三种方式(1).在介绍之前,来了解一下注入的概念:IoC(控制反转)即依赖注入,就是指程序在运行过程中,如果需要另外一个对象协助完成时,无需在代码中创建被调用者,而是依赖外部的注入获取。Spring的依赖注入对调用者几乎没有任何要求,完全支持对象之间的依赖关系的管理。(2).依赖注入通常有两种方式:设置注入、构造注入和属性注入—设置注入:设置注入是通过s…

    2022年6月17日
    25
  • thinkphp3.2如何自动生成后台模块并且怎么访问

    thinkphp3.2如何自动生成后台模块并且怎么访问

    2021年10月21日
    36
  • scikit-learn工具包中分类模型predict_proba、predict、decision_function用法详解「建议收藏」

    scikit-learn工具包中分类模型predict_proba、predict、decision_function用法详解「建议收藏」在使用sklearn训练完分类模型后,下一步就是要验证一下模型的预测结果,对于分类模型,sklearn中通常提供了predict_proba、predict、decision_function三种方法来展示模型对于输入样本的评判结果。说明一下,在sklearn中,对于训练好的分类模型,模型都有一个classes_属性,classes_属性中按顺序保存着训练样本的类别标记。下面是使用LogisticRegression分类器在为例,展示一下分类器的classes_属性。1、先看一下样本标签从0…

    2022年10月6日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号