java面试题:voliate底层原理——详解

java面试题:voliate底层原理——详解1.voliate底层原理1.1voliate变量的特点可见性:当一个线程修改了声明为volatile变量的值,新值对于其他要读该变量的线程来说是立即可见的。有序性:volatile变量的所谓有序性也就是被声明为volatile的变量的临界区代码的执行是有顺序的,即禁止指令重排序。受限原子性:volatile变量不可保证原子性1.2voliate如何实现变量多线程安全?实际上,voliate实现多线程情况下的变量安全其实就是通过以下两个方式:1)实现变量可见性2)禁止指令重

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

1. voliate底层原理

1.1 voliate变量的特点

  • 可见性: 当一个线程修改了声明为volatile变量的值,新值对于其他要读该变量的线程来说是立即可见的。
  • 有序性: volatile变量的所谓有序性也就是被声明为volatile的变量的临界区代码的执行是有顺序的,即禁止指令重排序。
  • 受限原子性: volatile变量不可保证原子性

1.2 voliate如何实现变量多线程安全?

  • 实际上,voliate实现多线程情况下的变量安全其实就是通过以下两个方式:
    1)实现变量可见性
    2)禁止指令重排序

1.3 voliate读写变量的过程

  • 写过程: 当一个线程修改某个voliate变量的值的时候,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。
  • 读过程: 当一个线程读取某个voliate变量的值的时候,JMM会把该线程对应的本地内存置为无效,线程接下来将从主内存读取共享变量。

1.4 voliate可见性底层实现原理

  • 实际上voliate的可见性实现借助了CPU的lock指令,即在写voliate变量的时候,在该指令前加一个lock指令,这个指令有两个作用:

    1)写volatile时处理器会将缓存写回到主内存。
    2)一个处理器的缓存写回到主内存会导致其他处理器的缓存失效。(即其他线程缓存该变量地址失效,下次读取时会自动从主存中读取)

如何通知其他线程的工作内存(缓存)失效

嗅探机制工作原理:每个处理器通过监听在总线上传播的数据来检查自己的缓存值是不是过期了,如果处理器发现自己缓存行对应的内存地址修改,就会将当前处理器的缓存行设置无效状态,当处理器对这个数据进行修改操作的时候,会重新从主内存中把数据读到处理器缓存中。

注意:基于 CPU 缓存一致性协议,JVM 实现了 volatile 的可见性,但由于总线嗅探机制,会不断的监听总线,如果大量使用 volatile 会引起总线风暴。所以,volatile 的使用要适合具体场景。

1.5 voliate有序性底层实现原理

  • volatile有序性的保证就是通过禁止指令重排序来实现的。指令重排序包括编译器和处理器重排序,JMM会分别限制这两种指令重排序。

  • 那么禁止指令重排序又是如何实现的呢?答案是加内存屏障。JMM为volatile加内存屏障有以下4种情况:

在这里插入图片描述

voliate的lock指令其实就相当于加了内存屏障,使用了volatile修饰变量,则对变量的写操作,会插入StoreLoad屏障。

上述内存屏障的插入策略是非常保守的,比如一个volatile的写操作前后需要加上StoreStore和StoreLoad屏障,但这个写volatile后面可能并没有读操作,因此理论上只加上StoreStore屏障就可以,的确,有的处理器就是这么做的。但JMM这种保守的内存屏障插入策略能够保证在任意的处理器平台,volatile变量都是有序的。

  • 总的来看,内存屏障就是在读写的操作前后加入两条指令,禁止指令重排序。但是这种插入策略比较保守,理论上写的后面可能没有读操作的话就只需要在前面加指令即可。

1.6 voliate为什么不保证原子性

首先说明i++的操作本身就不是原子性的,而是分为三步

1、线程读取i
2、i自增,temp = i + 1
3、刷回主存,i = temp

举例说明:
1)当 i=5 的时候A,B两个线程同时读入了 i 的值,
2)然后A线程执行了 temp = i + 1的操作, 要注意,此时的 i 的值还没有变化,
3)此时B线程也执行了 temp = i + 1的操作,注意,此时A,B两个线程保存的 i 的值都是5,temp 的值都是6,
4)然后A线程执行了 i = temp (6)的操作,此时i的值会立即刷新到主存并通知其他线程保存的 i 值失效,
5)此时B线程需要重新读取 i 的值那么此时B线程保存的 i 就是6,同时B线程保存的 temp 还仍然是6, 然后B线程执行 i=temp (6),所以导致了计算结果比预期少了1

也就是说B线程在自增之后(temp = 6),刷回主存之前,重新获取获取到了当前主存中最新的变量值6,但是此时自增操作已经完成了,这时候再重新将temp=6刷回主存,相当于B没有进行自增。

总之,其实voliate对变量的读写是线程安全的,但是变量的操作却不是线程安全的。

参考博客:https://www.jianshu.com/p/d7f78ac13c6a

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

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

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


相关推荐

  • java基础-云服务器购买

    java基础-云服务器购买小伙伴们,你们好呀!我是老寇!3年前,在阿里云买了我人生中的第一台云服务器,二话没说直接下单,看着支付宝的余额,我心如刀绞。所幸的是我熬过了这一个月。接下来我们进入正题(以阿里云为例)!目录一、操作步骤一、操作步骤1.输入阿里云网址,点击账号登录2.扫码登录->强烈建议下个阿里云APP,这样每次登陆只需要扫一扫就可以3.点击控制台,进入控制台4.完成实名认证(略)5.点击最新活动,找到开发者成长计划6.认准ECS服务器7.买Cento

    2022年5月5日
    41
  • phpstorm mac激活码2021详解【2021.7最新】

    (phpstorm mac激活码2021详解)最近有小伙伴私信我,问我这边有没有免费的intellijIdea的激活码,然后我将全栈君台教程分享给他了。激活成功之后他一直表示感谢,哈哈~IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.htmlML…

    2022年3月21日
    100
  • Tomcat国内镜像下载地址【速度超快】

    Tomcat国内镜像下载地址【速度超快】https://mirrors.cnnic.cn/apache/tomcat/ 选择想下载的版本,最新的就是那个tomcat-9 继续点进去 进去以后,要下载的可执行文件就在Bin目录下 exe结尾的就是安装文件了 …

    2022年5月18日
    43
  • 爬虫遇到js动态渲染问题

    爬虫遇到js动态渲染问题爬虫遇到js动态渲染问题时间:2020年6月3日10:28:48作者:钟健概要:关于scrapy爬虫应对网页JavaScript动态渲染问题关键字:scrapycrapy-splash一、传统爬虫的问题scrapy爬虫与传统爬虫一样,都是通过访问服务器端的网页,获取网页内容,最终都是通过对于网页内容的分析来获取数据,这样的弊端就在于他更适用于静态网页的爬取,而面对js渲染的动态网页就有点力不从心了,因为通过js渲染出来的动态网页的内容与网页文件内容是不一样的。1.实际案例腾讯招聘:ht

    2022年7月26日
    3
  • MySQL数据库笔记(三)

    MySQL数据库笔记(三)

    2021年7月10日
    73
  • C#窗口句柄

    C#窗口句柄在Windows中,句柄是一个系统内部数据结构的引用。例如当你操作一个窗口,或说是一个Delphi窗体时,系统会给你一个该窗口的句柄,系统会通知你:你正在操作142号窗口,就此你的应用程序就能要求系统对142号窗口进行操作——移动窗口、改变窗口大小、把窗口极小化为图标等。实际上许多WindowsAPI函数把句柄作为它的第一个参数,如GDI(图形设备接口)句柄、菜单句柄、实例句柄、位图句柄等,不仅仅局限于窗口函数。换句话说,句柄是一种内部代码,通过它能引用受系统控制的特殊元素,如窗口、位图、图标、内存块、

    2022年7月14日
    14

发表回复

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

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