Java内存模型与Volatile,Happen-Before原则等
Java的内存模型
Java内存模型(JMM)是一个抽象的模型。决定了线程主要定义了线程和内存间的抽象关系:主内存存放的是线程共享变量,每个线程有自己的工作内存,存放变量的副本,只能对副本进行读写,副本的变量再刷新到主内存中。具体体现为多核CPU,每核有一个高速缓存,每个核的线程对高速缓存读写,并且有共同的主存。
主内存与工作线程交互的操作有以下八种:
lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占的状态
unlock(解锁):作用于主内存的变量,释放锁定状态的变量
read(读取):作用于主内存的变量,把一个变量从主内存传输到线程的工作内存中,以便随后的load动作使用
load(载入):作用于工作内存的变量,把read操作从主内存中得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存的变量,把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作
assign(赋值):作用于工作内存的变量,把一个从执行引擎收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时将会执行这个操作
store(存储):作用于工作内存的变量,把工作内存的一个变量值传送到主内存,以便随后的write操作使用
write(写入):作用于主内存的变量,把store操作从工作内存得到的变量的值放入主内存变量中
上述八种操作均是原子操作。

如上图,若A和B两个线程同时去主存读写变量C,就会存在线程安全问题(可见性和有序性)。
Happens-Before原则
在JMM中,两个线程操作之间存在happens-before关系,则前一个操作的结果对后续操作可见。即使编译器进行指令重排序的优化,如果结果和重排序前一致,也是允许的。
因为允许指令重排序,这也说明 happens-before并不代表操作的时间顺序。
有如下8条规则:
1.程序次序规则:单线程内,按照程序顺序,书写在前面的操作 happens-before 于书写在后面的操作;
2.volatile变量规则:对一个变量的写操作 happens-before 于后面对这个变量的读操作(保证了volatile变量的可见型);
3.传递规则:如果 A happens-before B,而B happens-before C,则A happens-before C;
4.锁定规则:一个unLock操作happens-before 后面对同一个锁的lock操作;
5.线程启动规则:A线程调用B线程的B.start()方法和调用之前的操作 happens-before 于B线程中的任意操作;
6.线程终结规则:线程中所有的操作都 happens-before 于线程的终止检测,如B线程的操作都happens-before 于B.join();
7.线程中断规则:对线程interrupt()方法的调用 happens-before 于被中断线程的代码检测到中断事件的发生;
8.对象终结规则:一个对象的初始化完成 happens-before 他的finalize()方法的开始
Volatile关键词
java1.5之后,通过happen-before原则增强了volatile关键词。volatile关键词是轻量的实现线程安全的方法,保证了volatile变量的有序性和可见性。
1.volatile保证了变量在线程间的可见性( MESI 协议):
当写 volatile 变量时,JMM 会立即该线程对应的本地内存中的共享变量值刷新到主内存。
当读 volatile 变量时,JMM 会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。
volatile 保证内存可见性,其实是用到了 CPU 保证缓存一致性的 MESI 协议。当某线程对 volatile 变量的修改会立即回写到主存中,并且导致其他线程的缓存行失效,强制其他线程再使用变量时,需要从主存中读取。
2.volatile保证了有序性(内存屏障):
volatile变量在读写操作前后添加内存屏障,禁止了指令的重排序,保证了有序性。
内存屏障有LoadLoad屏障,StoreStore屏障,LoadStore屏障,StoreLoad屏障。比如loadload屏障,加在Load1; Load2两个原子操作之间 ,保证在Load2及后续的读操作读取之前,Load1已经读取。其他同理。
在每个volatile写入之前,插入一个StoreStore,写入之后,插入一个StoreLoad
在每个volatile读取之前,插入LoadLoad,之后插入LoadStore。
禁止重排序规则如下图。

3.volatile无法保证原子性,只能保证自身读写为原子操作。
对volatile变量的读写 相当与给读写方法加了synchronized关键词,但volatile更轻量级。
例子
最经典的例子就是单例模式的双重检查锁模式

这里instance变量需要设置为volatile类型,这样可以保证它的new操作不会被重排序。
因为这段代码可能在重排序后分为下列三步执行:
- 为 instance分配内存空间
- 将 instance指向分配的内存地址
- 初始化 instance
这样在执行2时,别的线程就会判断instance不为null,直接返回还未实例化完全的instance。
并且可以知道volatile修饰的instance变量的new操作并不是原子操作,否则也就不需要synchronized加锁来保证原子性了。
总结:
JMM内存模型定义了线程和内存间的抽象关系,实际的例子就是cpu核线程,高速缓存和主存间的关系;
happens-before原则的规定保证了操作间的可见性。
volatile变量保证了有序性和可见性。主要是内存屏障和MESI 协议实现的。volatile变量的读写操作为原子操作,本身其他操作(如自增)是非原子操作。
Java内存模型与Volatile,Happen-Before原则等的更多相关文章
- Java并发编程:JMM(Java内存模型)和volatile
1. 并发编程的3个概念 并发编程时,要想并发程序正确地执行,必须要保证原子性.可见性和有序性.只要有一个没有被保证,就有可能会导致程序运行不正确. 1.1. 原子性 原子性:即一个或多个操作要么全部 ...
- Java内存模型:volatile详解
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt202 Java内存模型:volatile是干什么用的Volatile字段是用 ...
- Java内存模型与volatile关键字
Java内存模型与volatile关键字 一).并发程序开发 并行程序的开发要涉及多线程.多任务间的协作和数据共享问题. 常用的并发控制:内部锁.重入锁.读写锁.信号量. 二).线程的特点 线程的特点 ...
- Java内存模型中volatile关键字的作用
volatile作用总结: 1. 强制线程从公共内存中取得变量的值,而不是从线程的私有的本地内存中,volatile修饰的变量不具有原子性(修改一个变量的值不能同步). 2. 保证volatile修饰 ...
- Java内存模型以及Volatile、Synchronize关键字的疑问
1.众所周知,java的内存模型是一个主内存,每个线程都有一个工作内存空间,那么主内存同步到工作内存是什么时候发生的呢?工作内存同步会主内存又是什么时候发生的呢? 在cpu进行线程切换时就会发生这些同 ...
- 【java】java内存模型(2)--volatile内存语义详解
多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”.可见性的意思是当一个线程 ...
- java内存模型与volatile变量与Atomic的compareAndSet
java分主内存和工作内存, 主内存是线程共享的, 工作内存是每个线程独有的. java对主内存的操作是通过工作内存间接完成的: 先拷贝主内存变量值到工作内存, 在工作内存操作这个变量的副本, 完成后 ...
- java内存模型-volatile
volatile 的特性 当我们声明共享变量为 volatile 后,对这个变量的读/写将会很特别.理解 volatile 特性的一个好方法是:把对 volatile 变量的单个读/写,看成是使用同一 ...
- 深入理解Java内存模型(四)——volatile
volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个监视器锁对这 ...
随机推荐
- 结队编程--java实现
1.GitHub地址:https://github.com/caiyouling/Myapp 队友:钟小敏 GitHub地址:https://github.com/zhongxiao136/Myapp ...
- 元素定位方法之Uiautomator方法
这个方法只能用于安卓系统,方法通过类UiSelector()来构造对象的 官网地址:https://developer.android.google.cn/topic/libraries/testin ...
- Upgrade Windows Server 2016 to Windows Server 2019
Pre-Upgrade Upgrade path: Windows Server 2016 can be upgraded to Windows 2019 in a single upgrade pr ...
- 06-jQuery进阶
本篇主要介绍jQuery的正则.冒泡事件.委托事件.以及DOM操作.JavaScript对象以及ajax等知识: 一.正则 简而言之,正则的规则无论是各种语言均是通用的,故其规则中的字符便不再介绍了, ...
- CSS选择器知识梳理
<一>CSS选择器结构逻辑图 温馨提示:各位小伙伴,可以把逻辑图下载下来放大,看的比较清除,也可以给我留言,我分享原百度脑图给各位小伙伴. <二>接下来按照结构逻辑图具体讲解各 ...
- 自动生成百度小程序sitemap.txt文件路径
因为业务需要,需要在目前项目上开发一个百度小程序,百度智能小程序上线了,但是内容每天得推送,不可能一个小程序路径一个推送吧,因为小程序路径和项目路径不一致. 因为项目是用ThinkPHP开发的,在此附 ...
- django-签名加密模块It's dangerous--加密token
https://juejin.im/entry/56b30250df0eea0054375e1d 安装 pip install itsdangerous 使用 from itsdangerous im ...
- The Open Source Business Model is Under Siege
https://www.influxdata.com/blog/the-open-source-database-business-model-is-under-siege/ A few weeks ...
- 【比赛题解】CSP2019 简要题解
D1T1 code 签到题,大家都会. 可以从高位往低位确定,如果遇到 \(1\),则将排名取反一下. 注意要开 unsigned long long. #include <bits/stdc+ ...
- 12-Flutter移动电商实战-首页导航区域编写
1.导航单元素的编写 从外部看,导航是一个GridView部件,但是每一个导航又是一个上下关系的Column.小伙伴们都知道Flutter有多层嵌套的问题,如果我们都写在一个组件里,那势必造成嵌套严重 ...