来源:衡阳网站优化

在java核心卷1中对volatile关键字是这么描述的:

volatile关键字为实例域的同步访问提供了一种免锁机制。如果声明一个域为volatile,那么编译器和虚拟机就知道该域是可能被另一个线程并发更新的。

上述解释可以通过下面代码直观的描述:

public class VolatileAtomicSample {
static Logger logger = Logger.getLogger(VolatileAtomicSample.class); private static boolean initFlag = false; public static void refresh() {
logger.info("refresh data ......");
initFlag = true;
logger.info("refresh data success ......");
} public static void loadData() {
while (!initFlag) { }
logger.info("线程:" + Thread.currentThread().getName()
+ "当前线程嗅探到initFlag的状态的改变!");
} public static void main(String[] args) {
Thread threadA = new Thread(VolatileAtomicSample::loadData,"threadA");
threadA.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
} Thread threadB = new Thread(() -> {
refresh();
}, "threadB");
threadB.start();
}

上面的代码中线程B执行了refresh方法,貌似initFlag的值被改成了true,但是执行的结果仍然是:

refresh data ......

refresh data success ......

并没有打印:线程:threadA当前线程嗅探到initFlag的状态的改变!

如果将变量initFlag 用关键字 volatile 修饰,那么结果就是:

19/12/21 20:58:15 INFO volatile1.VolatileAtomicSample: refresh data ......
19/12/21 20:58:15 INFO volatile1.VolatileAtomicSample: refresh data success ......
19/12/21 20:58:15 INFO volatile1.VolatileAtomicSample: 线程:threadA当前线程嗅探到initFlag的状态的改变!

那么volatile的工作原理是什么,在底层是如何执行的。

可以通过一个图来描述:

主内存简称“主存” ,工作内存可以叫做线程的缓存。

线程A将initFlag先read出来,然后load到线程A的缓存中去,然后use就是交给代码来使用。

线程B也按线程A的方式来使用initFlag。

但是线程A,B之间是没有可见性的。所以,线程A读出来的initFlag=flase,是在线程A的工作内存中操作,线程B读出来的initFlag是在线程B的工作内存中操作,也是initFlag=flase,就算线程B将initFlag重新赋值成true,线程A也不知道,就出现了第一个结果。

如果将initFlag加上一个volatile修饰符,那么线程B的中将initFlag的值改成true,就会执行assign,将true从代码部分放回线程B的工作内存,然后线程B的工作内存回执行store,然后再将修改的数据write到主存中去,这个时候,各个线程回对总线做监听,这个总线可以理解成一条河,那么所有线程都从总线中知道initFlag的状态发生改变了。然后每个对initFlag操作的工作线程都停止自己的操作,重新从主存中获取最新的数据再操作。

所以就是下面的这个图:

volatile 的工作原理就是上面的样子。

画外音:图确实画的有点渣。

 

最新文章

  1. Codeforces Round #202 (Div. 2) A,B
  2. go1.6.2 linux/amd64 的一个bug: gcc: 无法识别的选项‘-no-pie’
  3. iface eth0 inet dhcp
  4. Class Object
  5. [.NET WebAPI系列03] WebAPI Controller 中标准CRUD方法
  6. Json文件/网址解析
  7. uvalive 3890 Most Distant Point from the Sea
  8. 用JSTL简化Java Web开发
  9. 分享几个免费的开源邮件server软件
  10. linux内核之链表操作解析
  11. Ubuntu“无法解析或打开软件包的列表或是状态文件”的解决办法。_StarSasumi_新浪博客
  12. VSTO 基础随笔
  13. socket通讯,TCP,UDP,HTTP的区别
  14. 自学Python3.3-函数分类(内置函数补充)
  15. python迭代-可迭代对象与迭代器对象
  16. js空数组
  17. mysql 数据库操作 数据库的增删改查
  18. Scrum立会报告+燃尽图(Final阶段第七次)
  19. SimpleRoundedImage-不使用mask实现圆角矩形图片
  20. JS或AS中处理ARGB、RGBA颜色值时要小心

热门文章

  1. storm on yarn安装时 提交到yarn失败 failed
  2. cmake的find_package()简单总结
  3. Centos7.4系统 httpd模式搭建文件服务器
  4. Golang---sort包
  5. 2020/2/3 PHP代码审计之PHP伪协议
  6. 2020/1/31 PHP代码审计之文件包含漏洞
  7. POJ 1129:Channel Allocation 四色定理+暴力搜索
  8. 51nod 1013:3的幂的和 快速幂
  9. Socket通讯的简单用法
  10. SpringCloud学习之Bus消息总线实现配置自动刷新(九)