java面试题:voliate底层原理——详解
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
java面试题:voliate底层原理——详解相关推荐
- 并发编程五:java并发线程池底层原理详解和源码分析
文章目录 java并发线程池底层原理详解和源码分析 线程和线程池性能对比 Executors创建的三种线程池分析 自定义线程池分析 线程池源码分析 继承关系 ThreadPoolExecutor源码分 ...
- Java NIO 底层原理详解
写在前面 很多的小伙伴,被java IO 模型,搞得有点儿晕,一会儿是4种模型,一会儿又变成了5种模型. 很多的小伙伴,也被nio这个名词搞晕了,一会儿java 的nio 不叫 非阻塞io,一会儿ja ...
- 【你好面试官】008 Java内存模型指volatile底层原理详解、多处理器原子操作实现原理
微信公众号:你好面试官 这里没有碎片化的知识,只有完整的知识体系. 专注于系统全面的知识点讲解,面试题目解析; 如果你觉得文章对你有帮助,欢迎关注.分享.赞赏. ###前言 二蛋几天没有收到面试通知, ...
- 动态代理——拦截器——责任链——AOP面向切面编程底层原理详解(迪丽热巴版)
目录 动态代理模式详解 前言 什么是代理模式 如何进行代理 静态代理 动态代理 JDK动态代理 CGLIB动态代理 拦截器 责任链模式 博客文章版权申明 动态代理模式详解 前言 代理模式是设计模式中非 ...
- Java集合篇:Hashtable原理详解(JDK1.8)
(本文使用的源码基于JDK1.8的) 一.Hashtable的基本方法: 这部分参考博客:https://blog.csdn.net/chenssy/article/details/22896871 ...
- 大三专科实习第一个月——HTTPS底层原理详解
简介:脱变从现在开始,以上文章讲述的是广度问题接下来方向将是深度的问题.觉得我还可以的可以加群探讨技术QQ群:1076570504 个人学习资料库http://www.aolanghs.com/ 微信 ...
- Java多线程读写锁ReentrantReadWriteLock原理详解
ReentrantLock属于排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个线程访问,但是在写线程访问时,所有的读和其他写线程都被阻塞.读写锁维护了一对锁,一个读锁和一 ...
- Java中Unsafe类的原理详解与使用案例
点击关注公众号,利用碎片时间学习 1 概述 本文基于JDK1.8. Unsafe类位于rt.jar包,Unsafe类提供了硬件级别的原子操作,类中的方法都是native方法,它们使用JNI的方式访问本 ...
- 镜像底层原理详解和基于Docker file创建镜像
目录 一.镜像底层原理 1.联合文件系统(UnionFS) 2.镜像加载原理 3.为什么Docker里的centos的大小才200M? 二.Dockerfile 1.简介 2.Dockerfile操作 ...
最新文章
- robots.txt文件的解析及过滤
- 绩效面谈流程,阿里是这样做的
- 为 Virtual SAN 就绪节点和就绪块做好准备
- Oracle RAC CSS 超时计算 及 参数 misscount, Disktimeout 说明
- linux下日志rorate,[转载]linux下日志分割logrotate 设置和理解
- es6 Iterator(迭代器)的概念
- 在 Laravel 应用中使用 pjax 进行页面加速
- 安装 pear、phpunit 测试用例步骤方法
- L1-064 估值一亿的AI核心代码 (20 分)—团体程序设计天梯赛
- 如何选择合适的代理IP?以下3点需要注意
- QQ群技术:0成本创建2000人QQ群技巧
- win11登录网站出现登录失败,请使用IE内核浏览器解决办法
- 大数据杀熟行为10月1日起明令禁止!
- CCS:Type region `APP_CODE_MEM' overflowed by 641240 b
- pandas 转换为文本类型_python – pandas将文本特征转换为数值
- 计算机考研380分难吗,考研380分相当于高考多少分的难度
- [__ob__: Observer]
- 一位原码乘法器 一位补码乘法器原理
- 【JUC 并发编程】JUC 基本概念
- 【注释张豪华版 Path酷炫动画】极速get花式Path (支付宝支付成功动画)