2019独角兽企业重金招聘Python工程师标准>>>

最近一直在看《Java并发编程的艺术》这本书,看了后有种感觉,网上关于JVM与JUC的绝大部分文章、资料的源头都出自这本书以及《深入理解Java虚拟机》。作为一个Javaer,我应该好好多撸几遍这两本书且多做笔记。下面开始吧~

先看一段代码:

public class TestVolatile {public static void main(String[] args) {ThreadDemo threadDemo = new ThreadDemo();new Thread(threadDemo, "Thread-A").start();while (true) {if (threadDemo.isFlag()) {System.out.println(Thread.currentThread().getName() + "=============");break;}}}}class ThreadDemo implements Runnable {private boolean flag = false;@Overridepublic void run() {try {Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}flag = true;System.out.println("flag = " + isFlag());}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}
}

输出是什么?为什么?

发现flag的值已经被改为true了,但是Main线程中的while循环并未结束,这是什么原因导致的?

该问题被称为 内存可见性问题 ,即多个线程操作共享数据时,线程之间不能看到共享变量被修改后的

值,出现的原因就涉及到JVM内存模型(JMM)了:

Java中,所有的共享变量(实例域、静态域和数组)都存在堆内存中,因为堆内存在线程之间是共享的,所以这些变量被称为共享变量,JVM定义了线程与主内存(这里可以理解为就是堆内存)之间的一种抽象关系:线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地工作内存(也可以理解为缓存,主要是为了解决效率问题),本地内存中存储了该线程以读/写共享变量的副本

关于CPU操作主内存、本地工作内存可以类比一下CPU、高速缓存、内存之间的关系

线程A与线程B之间要通信(相互更改共享变量的值后让对方知道)的话,必须经历下面2个步骤:

  • 线程A把本地工作内存A中更新过的共享变量刷新到主内存中去
  • 线程B到主内存中去读取线程A之前已经更新过的共享变量

上述问题原因分析:

  1. 一开始线程Thread-A与main线程之间都有共享变量副本值flag = false
  2. 线程Thread-A将本地工作内存flag值改为flag = true,此时并不会立即更新到主内存中去
  3. main运行时,一直是用的是最开始从主内存中获取的flag = false,保存在本地工作内存中的, 所以while循环一直未能break

解决办法:

Java提供了volatile关键字,如果一个字段被声明为volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的。

有volatile变量修饰的共享变量进行写操作的时候,JVM执行字节码转化为汇编指令代码时,会多出lock前缀的一行汇编代码,Lock前缀的指令在多和多核处理器下会引发两件事情:

  1. 将当前处理器缓存行(本地工作内存)的数据写回到系统内存(主内存) 对应:当写一个volatile变量时,JMM会把该线程对应的本地工作内存中的共享变量值刷新到主内中。

  2. 这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效 对应:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内中读取共享变量。

volatile有许多特性:

  1. 可见性。对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量的最后的写入。什么意思?即当一个线程修改了变量的值,新的值会立即同步到主内存当中。而其他线程读取这个变量的时候,也会从主内存中拉取最新的变量值。

  2. 原子性。对任意单个volatile变量的读写具有原子性,但类似于volatile++这种复合操作不具有原子性

为什么volatile可以有这样的特性?(先大概了解下,后面可能会详细学习记录happens-before原则)

先行发生原则(happens-before),从JDK1.5开始,Java使用新的JSR-133内存模型,用happens-before原则来阐述线程之间的可见性,在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系

与程序员密切相关的happens-before规则如下:

  • 程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。
  • 监视器规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
  • volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
  • 传递性:如果A happens-before B,且B happens-before C ,那么A happens-before C。

了解完上述知识点后,再回到原来的问题,将flag用volatile修饰再运行验证下结论

public class TestVolatile {public static void main(String[] args) {ThreadDemo threadDemo = new ThreadDemo();new Thread(threadDemo, "Thread-A").start();while (true) {if (threadDemo.isFlag()) {System.out.println(Thread.currentThread().getName() + "=============");break;}}}}class ThreadDemo implements Runnable {private volatile boolean flag = false;@Overridepublic void run() {try {Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}flag = true;System.out.println("flag = " + isFlag());}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}
}

正是由于 volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。使得main线程读取flag的值时,一定在Thread-A写之后,所以能读到最新的修改后的值。

转载于:https://my.oschina.net/hensemlee/blog/1807436

JUC之volatile相关推荐

  1. 【学习笔记】多线程进阶JUC

    JUC多线程进阶 1.什么是JUC 源码 + 官方文档 JUC是 java util concurrent 业务:普通的线程代码 Thread Runnable: 没有返回值.效率相比于Callabl ...

  2. 并发编程——JUC并发编程知识脑图

    摘要 并发编程在软件编程中尤为突出和重要,在当今面试或工作中也是不可缺少的.作为一名高级java开发工程师,并发编程的技能已经成为了重要的一项.本博文将详细介绍并发编程中的知识点和知识脑图,帮助大家更 ...

  3. Seeker的奇妙求职历险(腾讯医疗一面和网易有道一面)

    前言 没想到PCG一面挂了之后被CSIG的医疗部门捞了,又面了一次,面完之后状态是显示等待复试,但是一直没消息,估计和阿里一样把我晾着吧. 网易有道面试感觉还算和面试官交谈甚欢,但是面完之后到现在都没 ...

  4. 浅谈Java锁,与JUC的常用类,集合安全类,常用辅助类,读写锁,阻塞队列,线程池,ForkJoin,volatile,单例模式不安全,CAS,各种锁

    浅谈JUC的常用类 JUC就是java.util.concurrent-包下的类 回顾多线程 Java默认有几个线程? 2 个 mian.GC Java 真的可以开启线程吗? 开不了,点击源码得知:本 ...

  5. JUC多线程:JMM内存模型与volatile内存语义

    一.JMM 内存模型: 1.什么是 JMM 内存模型: Java 内存模型是 Java 虚拟机定义的一种多线程访问 Java 内存各个变量的访问规范,主要围绕如何解决并发过程中的原子性.可见性.有序性 ...

  6. JUC笔记(synch、ReentrantLock、volatile等)

    JMM模型,定义了线程和主内存之间的抽象观念,主内存存储共享变量,每个线程拥有独立的工作内存存储共享变量的副本 多线程下存在问题 每次对变量的操作,在没有刷新到主内存前,其他线程不可见,会导致在多线程 ...

  7. 掌握Volatile关键字及其牵扯的JUC并发包

    熬不过的日子就让自己忙碌起来 目录 Volatile三大特性 保证可见性 JMM内存模型 原理 不保证原子性 原因 Atomic原子类 禁止指令重排 内存屏障 应用场景 状态标识 一次性安全发布 独立 ...

  8. Java——聊聊JUC中的volatile与内存屏障

    文章目录: 1.volatile内存语义 2.内存屏障 2.1 粗分两种:写屏障 2.2 粗分两种:读屏障 2.3 细分四种 3.volatile可见性介绍 4.volatile无原子性介绍 5.vo ...

  9. 从底层吃透java内存模型(JMM)、volatile、CAS

    前言 随着计算机的飞速发展,cpu从单核到四核,八核.在2020年中国网民数预计将达到11亿人.这些数据都意味着,作为一名java程序员,必须要掌握多线程开发,谈及多线程,绕不开的是对JMM(Java ...

  10. 面试高频——JUC并发工具包快速上手(超详细总结)

    目录 一.什么是JUC 二.基本知识 2.1.进程和线程 2.2.Java默认有两个进程 2.3.Java能够开启线程吗? 2.4.并发和并行 2.5.线程的状态 2.6.wait和sleep的区别 ...

最新文章

  1. C++之stdafx.h的用法说明
  2. 达内TTS6.0课件oop_day01
  3. JS 打印 data数据_数据表格 Data Table - 复杂内容的15个设计点
  4. SuperSocket 1.4系列文档(17) 在Windows Azure中运行SuperSocket
  5. 《C和指针》——指针运算
  6. 同期两篇 Nature:运行温度高于 1K 的量子计算平台问世!
  7. 签名相关文件是公司机密,在专机上由专人进行操作
  8. 对象 替换_JVM 对象分配过程
  9. Visio简单画图使用方法
  10. 《PRML》学习笔记2.2——多项式分布和狄利克雷分布
  11. 2019年美赛A题翻译与思路详解
  12. 数据分析 告诉你《飞驰人生》为什么这么燃?
  13. LINUX下运行.sh文件出现Syntax error: end of file unexpected (expecting “then”)最方便解决方法
  14. python学习之类
  15. 网络上找不到共享的计算机,找不到局域网中的共享电脑
  16. go语言学习:语言简介
  17. 2021年安全员-C证考试及安全员-C证考试技巧
  18. pyecharts制作地图Map
  19. 优惠券使用---责任链模式
  20. Bootstrap实战 - 单页面网站

热门文章

  1. Android 使用URLConnection来post数据
  2. 9.腾讯微博Android客户端开发——发送微博
  3. NET对象的XML序列化和反序列化
  4. OpenCV学习(十四)之彩色图像RGB通道的分离、合并与显示
  5. 项目心得--我的尺寸测量项目
  6. 魔方机器人之下位机编程------下位机完整程序
  7. python 合并与连接
  8. 深度学习笔记--激活函数:sigmoid,maxout
  9. 【统计分析】2 地理统计
  10. ppt 2016 html,2016第1章HTML5.ppt