Java并发—— 关键字volatile解析
简述
关键字volatile可以说是Java虚拟机提供的最轻量级的同步机制,当一个变量定义为volatile,它具有内存可见性以及禁止指令重排序两大特性,为了更好地了解volatile关键字,我们可以先看Java内存模型
Java内存模型
Java内存模型规定了所有的变量都存储在主内存中,每条线程拥有自己的工作内存,工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读写)都必须在工作内存中进行,不同的线程之间无法直接访问对方工作内存的变量。线程、主内存、工作内存关系:
以经典的i++为例,线程A从主内存获取变量i值放入到工作内存的变量副本,然后在工作内存中将i+1,最后将新值同步到主内存中。从中我们可以看出简单的i++,分了3个步骤,可以明显发现在线程A从主内存获取i值步骤后,可能有其他线程同步主内存中变量i的值,当线程A想要将i+1结果同步到主内存时就会出现不正确的结果,这是典型的线程不安全。
volatile特性
- 可见性
当一个线程修改了共享变量,其他线程能够立即得知这个修改。Java内存模型通过在变量修改后将新值同步回主内存,volatile变量能保证新值能立即同步到主内存,以及每次使用前立即从主内存刷新(synchronized和final两个关键字也具备)。还是拿i++为例,volatile修饰的i可以确保,从主存中所获取的变量i一定是最新的。
- 有序性
禁止指令重排序,程序执行的顺序按照代码的先后顺序执行。
在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。
①.编译器重排序:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序
②.处理器重排序:如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序
从java源代码到最终实际执行的指令序列,会分别经历下面三种重排序:
1属于编译器重排序,2和3属于处理器重排序。
volatile使用场景
在某些特定场景中,volatile相当于一个轻量级的sychronize,因为不会引起线程的上下文切换,但是使用volatile必须满足两个条件:
①.运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值
②.变量不需要与其他的状态变量共同参与不变约束
两个使用场景:
- 状态标记
使用volatile变量来控制并发,当shutdown()方法被调用时,能保证所有线程中执行的doWork()方法都立即停下来
public class VolatileTest {
private volatile boolean shutdownRequested;
public void shutdown() {
shutdownRequested = true;
}
public void doWork(){
while (!shutdownRequested) {
// 业务逻辑
}
}
}
复制代码
复制代码
复制代码
- DCL(双锁检测)
单例模式的一种实现方式
public class Singleton {
private volatile static Singleton singleton;
public static Singleton getInstance() {
if(singleton == null){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
复制代码
复制代码
复制代码
volatile实现
从硬件架构上来讲,处理器使用写缓冲区来临时保存向内存写入的数据,可以减少对内存总线的占用。虽然写缓冲区有这么多好处,但此操作仅对它所在的处理器可见,这个特性会对内存操作的执行顺序产生重要的影响,由于操作缓冲区是异步操作所以在外面看来,先写后读,还是先读后写,没有严格的固定顺序。
volatile修饰的变量相对于普通变量会多出一个lock前缀指令,这个操作相当于一个内存屏障(只有一个CPU访问内存时,不需要内存屏障;但如果有两个或更多CPU访问同一块内存,且其中有一个在观测另一个,就需要内存屏障来保证一致性)。
| 是否能重排序 | 第二个操作 | |||
| 第一个操作 | 普通读 | 普通写 | volatile读 | volatile写 |
| 普通读 | LoadStore | |||
| 普通写 | StoreStore | |||
| volatile读 | LoadLoad | LoadStore | LoadLoad | LoadStore |
| volatile写 | StoreLoad | StoreStore | ||
空白的单元格代表在不违反Java的基本语义下的重排是允许的。
StoreStore屏障:保证在volatile写之前,其前面的所有普通写操作都已经刷新到主内存
StoreLoad屏障:避免volatile写与后面可能有的volatile读/写操作重排序
LoadLoad屏障:禁止处理器吧上面的volatile读与下面的普通读中排序
LoadStore屏障:禁止处理器把上面的volatile读与下面的普通写重排序
示例:
public class VolatileTest {
int a = 0;
volatile int var1 = 1;
volatile int var2 = 2;
void readAndWrite() {
int i = var1; //volatile读
int j = var2; //volatile读
a = i + i; //普通读
var1 = i + 1; //volatile写
var2 = j * 2; //volatile写
}
复制代码
复制代码
} 复制代码
大致过程:
感谢
1.《深入理解Java虚拟机》
2.占小狼——面试必问的volatile,你了解多少?
3.《Java并发编程的艺术》
Java并发—— 关键字volatile解析的更多相关文章
- Java 并发 关键字volatile
Java 并发 关键字volatile @author ixenos volatile只是保证了共享变量的可见性,不保证同步操作的原子性 同步块 和 volatile 关键字机制 synchroniz ...
- Java并发关键字Volatile 详解
Java并发关键字Volatile 详解 问题引出: 1.Volatile是什么? 2.Volatile有哪些特性? 3.Volatile每个特性的底层实现原理是什么? 相关内容补充: 缓存一致性协议 ...
- Java并发——关键字synchronized解析
synchronized用法 在Java中,最简单粗暴的同步手段就是synchronized关键字,其同步的三种用法: ①.同步实例方法,锁是当前实例对象 ②.同步类方法,锁是当前类对象 ③.同步代码 ...
- java多线程关键字volatile的使用
java多线程关键字volatile的作用是表示多个线程对这个变量共享. 如果是只读的就可以直接用,写数据的时候要注意同步问题. 例子: package com.ming.thread.volatil ...
- Java并发编程 Volatile关键字解析
volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了 ...
- Java 并发:volatile 关键字解析
摘要: 在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保 ...
- java并发系列(六)-----Java并发:volatile关键字解析
在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保证可见性 ...
- java并发:volatile关键字
java并发需要保证原子性,可见性,有序性. http://www.cnblogs.com/expiator/p/9226775.html 一.volatile关键字作用如下: 1.volatile关 ...
- Java并发编程volatile关键字
volatile理解 Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和volatile 关键字机制.volatile具有synchronized关键字的“可见性”,vo ...
随机推荐
- iOS-【UIDynamic-UIKit动力学】
如果看不到图片 可以尝试更换浏览器(推荐Safari ) 0.了解 •Dynamic Animator:动画者,为动力学元素提供物理学相关的能力及动画,同时为这些元素提供相关的上下文,是动力学元素与底 ...
- 【PHP】- PHPStorm+XDebug进行调试图文教程
转载:https://www.cnblogs.com/LWMLWM/p/8251905.html 这篇文章主要为大家详细介绍了PHPStorm+XDebug进行调试图文教程,内容很丰富,具有一定的 ...
- 第五部分shell项目一监控脚本
需求: 使用shell定制各种个性化告警工具,但需要统一化管理.规范化管理. 思路:指定一个脚本包,包含主程序.子程序.配置文件.邮件引擎.输出日志等.主程序:作为整个脚本的入口,是整个系统的命脉.配 ...
- TreeView的使用
用于显示多级层次关系 每一项是一个节点,也就是一个Node,是一个TreeNode节点,Nodes是该控件节点的集合. selectedNode用户选中的节点,如果没有选中则为null 1. 当选中后 ...
- Gradle sync failed: Failed to find Build Tools revision 26.0.2的解决办法
说明在android studio中没有 build tools 的26.0.2的版本,你确认一下,是否是这样: 点击==>android studio的菜单栏中Tools==>andro ...
- matlab中prod的使用方法
B = prod(A) 将A矩阵不同维的元素的乘积返回到矩阵B. 如果A是向量,prod(A)返回A向量的乘积.如果A是矩阵,prod(A)返回A每一列元素的乘积并组成一个行向量B. B = prod ...
- BZOJ 1057 棋盘制作(最大01相间子矩阵)
求最大01相间子矩阵可以转换为求最大全0子矩阵.只需把棋盘(x+y)为奇数的取反,而该问题可以用经典的悬线法O(n^2)的求解. 悬线法呢. 首先定义b[i][j],为a[i][j]向上的最大连续0的 ...
- [BZOJ2067]szn
description BZOJ权限题. solution 一道非常好的二分+贪心题目. 第一问就是\(\frac{\sum_u(deg[u]-1)}{2}+1\). 第二问需要在方案最优的情况下最长 ...
- 51NOD 1934:受限制的排列——题解
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1934 听说会笛卡尔树的人这题都秒了啊…… 参考:https://blog ...
- [Leetcode] word search 单词查询
Given a 2D board and a word, find if the word exists in the grid. The word can be constructed from l ...