JUC之volatile
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之前已经更新过的共享变量
上述问题原因分析:
- 一开始线程Thread-A与main线程之间都有共享变量副本值flag = false
- 线程Thread-A将本地工作内存flag值改为flag = true,此时并不会立即更新到主内存中去
- main运行时,一直是用的是最开始从主内存中获取的flag = false,保存在本地工作内存中的, 所以while循环一直未能break
解决办法:
Java提供了volatile关键字,如果一个字段被声明为volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的。
有volatile变量修饰的共享变量进行写操作的时候,JVM执行字节码转化为汇编指令代码时,会多出lock前缀的一行汇编代码,Lock前缀的指令在多和多核处理器下会引发两件事情:
将当前处理器缓存行(本地工作内存)的数据写回到系统内存(主内存) 对应:当写一个volatile变量时,JMM会把该线程对应的本地工作内存中的共享变量值刷新到主内中。
这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效 对应:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内中读取共享变量。
volatile有许多特性:
可见性。对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量的最后的写入。什么意思?即当一个线程修改了变量的值,新的值会立即同步到主内存当中。而其他线程读取这个变量的时候,也会从主内存中拉取最新的变量值。
原子性。对任意单个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相关推荐
- 【学习笔记】多线程进阶JUC
JUC多线程进阶 1.什么是JUC 源码 + 官方文档 JUC是 java util concurrent 业务:普通的线程代码 Thread Runnable: 没有返回值.效率相比于Callabl ...
- 并发编程——JUC并发编程知识脑图
摘要 并发编程在软件编程中尤为突出和重要,在当今面试或工作中也是不可缺少的.作为一名高级java开发工程师,并发编程的技能已经成为了重要的一项.本博文将详细介绍并发编程中的知识点和知识脑图,帮助大家更 ...
- Seeker的奇妙求职历险(腾讯医疗一面和网易有道一面)
前言 没想到PCG一面挂了之后被CSIG的医疗部门捞了,又面了一次,面完之后状态是显示等待复试,但是一直没消息,估计和阿里一样把我晾着吧. 网易有道面试感觉还算和面试官交谈甚欢,但是面完之后到现在都没 ...
- 浅谈Java锁,与JUC的常用类,集合安全类,常用辅助类,读写锁,阻塞队列,线程池,ForkJoin,volatile,单例模式不安全,CAS,各种锁
浅谈JUC的常用类 JUC就是java.util.concurrent-包下的类 回顾多线程 Java默认有几个线程? 2 个 mian.GC Java 真的可以开启线程吗? 开不了,点击源码得知:本 ...
- JUC多线程:JMM内存模型与volatile内存语义
一.JMM 内存模型: 1.什么是 JMM 内存模型: Java 内存模型是 Java 虚拟机定义的一种多线程访问 Java 内存各个变量的访问规范,主要围绕如何解决并发过程中的原子性.可见性.有序性 ...
- JUC笔记(synch、ReentrantLock、volatile等)
JMM模型,定义了线程和主内存之间的抽象观念,主内存存储共享变量,每个线程拥有独立的工作内存存储共享变量的副本 多线程下存在问题 每次对变量的操作,在没有刷新到主内存前,其他线程不可见,会导致在多线程 ...
- 掌握Volatile关键字及其牵扯的JUC并发包
熬不过的日子就让自己忙碌起来 目录 Volatile三大特性 保证可见性 JMM内存模型 原理 不保证原子性 原因 Atomic原子类 禁止指令重排 内存屏障 应用场景 状态标识 一次性安全发布 独立 ...
- Java——聊聊JUC中的volatile与内存屏障
文章目录: 1.volatile内存语义 2.内存屏障 2.1 粗分两种:写屏障 2.2 粗分两种:读屏障 2.3 细分四种 3.volatile可见性介绍 4.volatile无原子性介绍 5.vo ...
- 从底层吃透java内存模型(JMM)、volatile、CAS
前言 随着计算机的飞速发展,cpu从单核到四核,八核.在2020年中国网民数预计将达到11亿人.这些数据都意味着,作为一名java程序员,必须要掌握多线程开发,谈及多线程,绕不开的是对JMM(Java ...
- 面试高频——JUC并发工具包快速上手(超详细总结)
目录 一.什么是JUC 二.基本知识 2.1.进程和线程 2.2.Java默认有两个进程 2.3.Java能够开启线程吗? 2.4.并发和并行 2.5.线程的状态 2.6.wait和sleep的区别 ...
最新文章
- C++之stdafx.h的用法说明
- 达内TTS6.0课件oop_day01
- JS 打印 data数据_数据表格 Data Table - 复杂内容的15个设计点
- SuperSocket 1.4系列文档(17) 在Windows Azure中运行SuperSocket
- 《C和指针》——指针运算
- 同期两篇 Nature:运行温度高于 1K 的量子计算平台问世!
- 签名相关文件是公司机密,在专机上由专人进行操作
- 对象 替换_JVM 对象分配过程
- Visio简单画图使用方法
- 《PRML》学习笔记2.2——多项式分布和狄利克雷分布
- 2019年美赛A题翻译与思路详解
- 数据分析 告诉你《飞驰人生》为什么这么燃?
- LINUX下运行.sh文件出现Syntax error: end of file unexpected (expecting “then”)最方便解决方法
- python学习之类
- 网络上找不到共享的计算机,找不到局域网中的共享电脑
- go语言学习:语言简介
- 2021年安全员-C证考试及安全员-C证考试技巧
- pyecharts制作地图Map
- 优惠券使用---责任链模式
- Bootstrap实战 - 单页面网站