前言

经常在网上看一些大牛们的博客,从中收获到一些东西的同时会产生一种崇拜感,从而萌发了自己写写博客的念头.然而已经有这个念头很久,却始终不敢下手开始写.今天算是迈出了人生的一大步^_^!


volatile的定义及其实现

定义:如果一个字段被声明成volatile,那么java线程内存模型将确保所有线程看到的这个变量的值都是一致的.

从它的定义当中咱们也可以了解到volatile具有可见性的特性.但它具体是如何保证其可见性的呢?

先看一段JIT编译器生成的汇编指令

//Java代码如下
instance = new Singleton(); //这里instance是volatile变量
//反汇编后
0x01a3de1d: movb $0x0,0x1104800(%esi);
0x01a3de24: lock add1 $0x0,(%esp);
复制代码

有volatile修饰的变量在进行写操作时会出现第二行反汇编代码,重点在lock这个指令.它有两个目的:

  1. 立即回写当前处理器缓存行的值到内存.
  2. 其他所有cpu缓存了该地址的数据将会失效.

这里大家也许会有疑问,有没有可能存在多个cpu一起回写数据?

答案是不会的.虽然cpu鼓励多个处理器可以有竞争,但是总线会对竞争做出裁决,只会有一个cpu获取优先权.其他处理器会被总线禁止,处于阻塞状态.如下图:

对于第二点,其他cpu缓存该地址的数据失效后想要再次使用的话就必须得从主内存中重新读取,这样就能保证再次执行计算时所获取的值是最新的,也可以认为所有CPU的缓存是一致的,这也就证明了volatile修饰的字段是可见的.


可见性不代表在并发下是安全的

这里咱们先引进一段代码:

/*** volatile 变量自增运算** @author mars*/
public class VolatileTest {public static volatile int count = 0;public static void increase() {count++;}private static final int THREAD_COUNTS = 20;public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(THREAD_COUNTS);Thread[] threads = new Thread[THREAD_COUNTS];for (int j = 0; j < THREAD_COUNTS; j++) {threads[j] = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 10000; i++) {increase();}latch.countDown();}});threads[j].start();}//等待所有的线程执行结束latch.await();System.out.println(count);}
}
复制代码

这段代码供发起了20个线程,对count变量进行了10000次自增操作,如果volatile修饰的字段在并发下是安全的话,讲道理最终结果都会是200000,但经过测试发现,每次的输出结果都会不一样.但具体是什么原因造成的?

其实最主要的问题是出在increase()这个自增方法上,这个操作不是一个原子操作,也就是不是一步就能操作完成的,其中会经历count值入栈,add,出栈,到操作线程缓存,最终到内存等等一系列步骤.当A线程其执行这些指令时,B线程正好将数据同步到了主内存中,此时A线程栈顶的数据就会变成过期数据,然后A线程就会将较小的值同步到主内存中.


如何正确的运用volatile

要想运用好volatile修饰符,需要保证运用场景符合下述规则:

  1. 运算结果不依赖变量的当前值.
  2. 该变量不需要和其他变量共同参与约束.

例如使用volatile变量来控制并发就很合适:

volatile boolean shutdownWork;public void shutdowm(){shutdownWork = true;}public void doWork(){while (!shutdownWork){//execute task}}
复制代码

上面这段代码运行结果并无需依赖shutdownWork的值,但是只要shutdownWork的值一旦经过改变,便会立即被其他所有线程所感知,然后停止执行任务.


小知识点

在多处理器下,为了保证各个处理器的缓存是一致的,处理器会使用嗅探技术来保证它的内部缓存,系统内存和其他处理器的缓存的数据在总线上保持一致.如果通过嗅探检测到其他处理器打算写内存地址,而这个地址当前处于共享状态,那么正在嗅探的处理器将使它的缓存无效,在下次访问相同的内存地址时,强制执行缓存行填充,也就是从内存中重新读取该内存地址指向的值.


End

浅析volatile原理及其使用相关推荐

  1. volatile原理与技巧

    转自http://www.iteye.com/topic/109150 volatile, 用更低的代价替代同步 为什么 使用volatile比同步代价更低?  同步的代价, 主要由其覆盖范围决定, ...

  2. 24.volatile关键字的作用、volatile原理、可见性、内存屏障、volatile性能、transient

    24.volatile关键字的作用 24.1.volatile原理 24.2.可见性 24.3.内存屏障 24.4.volatile性能 25.transient 24.volatile关键字的作用 ...

  3. 浅析bootstrap原理及优缺点

    网格系统的实现原理,是通过定义容器大小,平分12份(也有平分成24份或32份,但12份是最常见的),再调整内外边距,最后结合媒体查询,就制作出了强大的响应式网格系统 网格系统的实现原理,是通过定义容器 ...

  4. 手把手带你学会Odoo OWL组件开发(5):浅析OWL原理

    [本系列内容直达:] 手把手带你学习Odoo OWL组件开发(1):认识 OWL 手把手带你学会Odoo OWL组件开发(2):OWL的使用 手把手带你学会Odoo OWL组件开发(3):核心内容指南 ...

  5. 浅析Kerberos原理,及其应用和管理

    文章作者:luxianghao 文章来源:http://www.cnblogs.com/luxianghao/p/5269739.html  转载请注明,谢谢合作. 免责声明:文章内容仅代表个人观点, ...

  6. java volatile原理A CUP层面

    作者:知乎用户 链接:https://www.zhihu.com/question/49656589/answer/117826278 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业 ...

  7. 浅析ajax原理与用法

    1 ajax原理 Ajax(Asynchronous JavaScript and XML (异步的JavaScript和XML)),是一种快速创建 动态网页的技术,目的是显示动态局部刷新.通过XML ...

  8. TUN/TAP设备浅析(一) -- 原理浅析

    TUN/TAP设备浅析 TUN设备 TUN 设备是一种虚拟网络设备,通过此设备,程序可以方便地模拟网络行为.TUN 模拟的是一个三层设备,也就是说,通过它可以处理来自网络层的数据,更通俗一点的说,通过 ...

  9. 浅析ElasticSearch原理

    女主宣言 最近女主在项目中使用到ElasticSearch来做索引.但是对ElasticSearch的一些原理还是比较模糊,所以就梳理了一下ElasticSearch的基本原理,分享给大家. PS:丰 ...

最新文章

  1. 一套基础自动化部署搭建过程
  2. 矩阵转置 java_Java 创建矩阵并转置矩阵
  3. python考证书-计算机二级 Python 怎么考?考什么?
  4. iphone屏蔽系统更新_未越狱屏蔽系统更新还是来了
  5. 【观点】风雨20年:我所积累的20条编程经验
  6. Docker 实战教程之从入门到提高 (四)
  7. 目标检测——感受野的学习笔记
  8. 概念梳理:C++中iostream头文件和命名空间的基础介绍和拓展内容
  9. 再抱一抱DataStore
  10. 前端应届简历应该怎么写?
  11. 一封高三班主任写给大学生的信
  12. 力扣-无重复字符的最长子串
  13. 腾讯T-Star高校挑战赛
  14. 关键信息基础设施确定指南_干货分享 | 关键信息基础设施运营单位如何做好业务安全测试...
  15. python分析红楼梦中人物形象_《红楼梦》主要人物形象分析
  16. ROS2-Gazebo仿真
  17. Android 2018优秀开源框架整理收藏
  18. 利用正则表达式来验证邮箱
  19. PowerDesigner 把设计图导出成图片
  20. java epson指令集_小票打印机指令集封装(支持EPSON指令)

热门文章

  1. python中 __name__及__main()__的妙处02
  2. 创建 Java 第三方类库,以及制作类库的Javadoc
  3. 百度地图手绘线坐标获取
  4. Struts2异常| 页面写入s:debug/标签报错, 去掉此标签后正常显示
  5. PetaPoco 快速上手
  6. 数字对 (长乐一中模拟赛day2T2)
  7. vim for python
  8. ExtJS 开发调试工具大全
  9. android利用WebView实现浏览器的封装
  10. docker 集群中文件挂载的问题