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变量的单个读/写,看成是使用同一个监视器锁对这 ...
随机推荐
- 基于MUI框架+HTML5PLUS 开发 iOS和Android 应用程序(APP)
目录 事前准备 创建项目 利用MUI写一个简单的页面 关于文件打包 事前准备 # 软件 HBuilder X Web开发IDE 下载地址:https://www.dcloud.io/hbuilderx ...
- k8s维护常用命令
k8s维护 1. 不可调度 kubectl cordon k8s-node-1 kubectl uncordon k8s-node-1 #取消 2.驱逐已经运行的业务容器 kubectl drain ...
- HttpClient使用详解与实战一:普通的GET和POST请求
简介 HttpClient是Apache Jakarta Common下的子项目,用来提供高效的.最新的.功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议. Htt ...
- 嵌入式 vlc从接收到数据流到播放视频的过程分析(经典)
个人整理: Vlc流播放流程 vlc源码目录树: 目录名称 说明 bindings Java, CIL 和Python绑定 doc 帮助文档 (不是更新的) extras 另叙. include VL ...
- Pycharm中连接数据库乱码问题解决
当我们使用pycharm建立数据库之后,看到里面的数据都是乱码,就像下面一样: 其实这个并不是pycharm的显示问题,而是建立数据库时产生的. 解决方法是到指定字符集的命令提示符中重新建表并指定字符 ...
- MySQL-CentOS7上安装Mysql5.7
#安装 wget http://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm .noarch.rpm yum instal ...
- 图记 2016.1.7 获取本地图片、Bitmap转image
这几天完成的内容有: 1.“添加图片”按钮 2.添加图片功能 遇到的问题: 我想要将添加图片按钮放在右下角,所以采用了相对布局,但是问题随之二来,因为将导航栏设置成了半透明,所以图片放到右下角之后,半 ...
- Linux DNS 分离解析
设置DNS分离解析可以对不同的客户端提供不同的域名解析记录.来自不同地址的客户机请求同一域名时,为其提供不同的解析结果. 安装 bind 包 [root@localhost ~]# yum insta ...
- 什么影响了mysql的性能-硬件资源及系统方面优化
随着数据量的增大,数据库的性能问题也是个值得关注的问题,很多公司对mysql性能方面没有太过重视,导致服务浪费过多资源.mysql服务性能差从而直接影响用户体验,这里我们简单的先来聊聊什么影响了mys ...
- redis发布订阅实现各类定时业务(优惠券过期,商品不支付自动撤单,自动收货等)
修改redis配置文件找到机器上redis配置文件conf/redis.conf,新增一行 notify-keyspace-events Ex 最后的Ex代表 监听失效的键值 修改后效果如下图 代码 ...