浅析Volatile关键字
浅析Volatile关键字
在java中线程并发中,线程之间通信方式分为两种:共享内存和消息传递。共享内存指的是多个线程之间共享内存的属性状态;消息传递指的是线程之间发送信息来通信。在介绍volatile,我们先了解一下共享内存一些基本概念。
JMM
Java内存模型(简称JMM)控制线程通信,可以分为主内存和本地内存,每个线程拥有一个本地内存。

如图,一般主存只有一个,根据线程数不同,本地内存(又称工作内存)数目也不同,本地内存是抽象出来的概念,实际上不存在。
线程之间通过内存来同步状态可以分为以下几个步骤:
1、 线程1从主存获取变量x=1,假设此时其他线程x都为1

2、 线程1将本地内存中x值修改为2

3、 线程1将本地内存中x值放回主存

4、 其他线程同步主存数据

以上,就是理想状态线程之间通过主存通信状态,保证了内存的可见性。
指令重排序
java程序在运行时,并不会严格按照程序代码编写的顺序执行代码,会对其进行重排序。例如以下例子:

在程序执行代码的时候,步骤1和步骤2可能被重排序,导致2可能在1之前先执行,但是重排序的前提是在单线程情况下不会改变运行结果,因此1和2可以重排序,但是3引用了1和2中的变量,因此3不会被重排序到1或者2之前。在单线程情况下,重排序不会影响,但在多线程中会导致结果不可预见,可以使用其他方法来保证有序性。
原子性操作
前面提到了可见性,有序性和原子性是在并发编程中最常遇见的三个概念。原子性指不可分割的操作。举例说明,在32为JVM中长度小于32为的数据读写都是原子性操作,但是64位数据结构的读写不是原子性操作。例子如下:
1、 线程1和2中有共同变量x,为Long型

2、 线程1将x改为LONG_MAX/2

3、线程1在修改本地内存中的值到主存时,先将Long的前32保存到主存中,此时主存中的x值为LONG_MAX/2的前32为加上LONG_MAX后32为,我们设为z

4、线程2读取主存中的x值并更新

5、线程1同步后32位数据到主存中,之后线程3也同步主存数据

最后可以看到,由于64为数据读写不是原子性的,单只线程2获取的数据为错误数据,如果,线程1同步主存的操作为原子性,那么就不会出现以上情况。
Volatile关键字
Volatile能够保证程序的原子性,例如将Long或者Double设置为volatile可以避免由于非原子性读写造成的数据不同步。但是,类似于x = x+1;这种类型的复合操作,volatile依旧服务保证原子性。
1、 volatile关键字x,线程1将x修改为LONG_MAX/2,主存为z

2、此时,由于x是volatile,因此线程要从主存读取这个值的时候,并不会主动到主存中获取,而是等到其他线程通知他再去获取。因此,线程1将完整的x值写到主存中。

3、之后,通知其他线程去获取x的值,以实现数值同步,保证原子性

由于volatile的值会及时将值刷新到主存中,volatile也保证了可见性。
那么volatile是否能保证有序性?个人见解是能在一定程度上保证数据的可见性,但是没有了解到有资料明确说明volatile保证了原子性。
volatile实际上设置了内存屏障,由于内存屏障的存在,保证了多线程下不会由于重排序导致数据不一致的问题。
|
是否重排序 |
第二个操作 |
||
|
第一个操作 |
普通读写 |
Volatile读 |
Volatile写 |
|
普通读写 |
No |
||
|
Volatile读 |
No |
No |
No |
|
Volatile写 |
No |
No |
|
可知volatile写之前的操作不会被重排序到之后;volatile读之后的操作不会被重排序到之前;第一个操作是volatile写,第二个操作是volatile读不会重排序。
JMM采取的内存屏障策略为:
1、 在volatile写前面插入StoreStore屏障
2、 在volatile写后面插入StoreLoad屏障
3、 在volatile读后面插入LoadLoad屏障
4、 在volatile读后面插入LoadStore屏障
StoreStore屏障保证volatile写之前的普通写刷新到主存当中,避免与前面普通写重排序;
StoreLoad屏障保证volatile写不会和之后的volatile读写重排序;
LoadLoad屏障禁止volatile读不会与下面的普通读重排序;
LoadStore屏障禁止volatile读不会与下面的普通写重排序。
具体可见p1,p2

P1

P2
一个例子
如下例子,线程1执行writer方法,线程2执行reader方法,由于JVM会进行重排序,因此可能的执行顺序为:
1、线程1执行步骤2,此时a的值还是0
2、线程2执行步骤3,因为重排序不会在引起单线程结果改变,因此不会先执行4
3、线程2执行步骤4,i的值为0
4、线程1执行步骤1
Int a = 0
Boolean flag =false
public void writer(){
a = 1; //
flag = true; //
}
public void reader(){
if(flag){ //
int i = a; //
}
}
可见,由于重排序导致结果改变,修改代码如下:
Int a = 0
volatile boolean flag =false
public void writer(){
a = 1; //
flag = true; //
}
public void reader(){
if(flag){ //
int i = a; //
}
}
执行步骤如下:
1、 线程1执行1,因为volatile变量flag前面插入StoreStore屏障,不会重排序
2、 线程1执行2;
3、 线程2执行3;
4、 线程2执行4,i为2;
结果符合预期。
Normal
0
7.8 磅
0
2
false
false
false
EN-US
ZH-CN
X-NONE
/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.5pt;
mso-bidi-font-size:11.0pt;
font-family:"Calibri",sans-serif;
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:minor-latin;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:minor-latin;
mso-bidi-font-family:"Times New Roman";
mso-bidi-theme-font:minor-bidi;
mso-font-kerning:1.0pt;}
浅析Volatile关键字的更多相关文章
- Java并发编程:volatile关键字解析
Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 ...
- volatile关键字 学习记录2
public class VolatileTest2 implements Runnable{ volatile int resource = 0; public static void main(S ...
- volatile关键字 学习记录1
虽然已经工作了半年了...虽然一直是在做web开发....但是平时一直很少使用多线程..... 然后最近一直在看相关知识..所以就有了这篇文章 用例子来说明问题吧 public class Volat ...
- 【转】Java并发编程:volatile关键字解析
转自:http://www.importnew.com/18126.html#comment-487304 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备 ...
- 架构师养成记--4.volatile关键字
volatile修饰的变量可在多个线程间可见. 如下代码,在子线程运行期间主线程修改属性值并不对子线程产生影响,原因是子线程有自己独立的内存空间,其中有主内存中的变量副本. public class ...
- java中volatile关键字的含义
在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉. Java语言是支持多线程的,为了解决线程并发的问题,在语 ...
- volatile关键字详解
本文系转载,原文链接:http://www.cnblogs.com/Chase/archive/2010/07/05/1771700.html,如有侵权,请联系我:534624117@qq.com 引 ...
- volatile关键字并不能作为线程计数器
在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉. Java语言是支持多线程的,为了解决线程并发的问题,在语 ...
- volatile关键字及编译器指令乱序总结
本文简单介绍volatile关键字的使用,进而引出编译期间内存乱序的问题,并介绍了有效防止编译器内存乱序所带来的问题的解决方法,文中简单提了下CPU指令乱序的现象,但并没有深入讨论. 以下是我搭建的博 ...
随机推荐
- 原生js-input框全选
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- HTTP协议COOKIE和SESSION有什么区别
1.为什么会有COOKIE这种机制 首先一种场景, 在一个网站上面, 我发起一次请求,那服务器怎么知道我是谁?是谁发起的这次请求呢, HTTP协议是无状态的协议, 浏览器的每一次请求,服务器都当做一 ...
- 考试总结(橙题WA)
又逢校内测,成绩变化大 初见三道题,暗喜AK辣 谁知数据毒,特判不到家 三题两题WA,心态已爆炸 T1(我不想再见到这道题): 附上多年前AC但是随便出(毒瘤)一组数据就可以卡掉的代码: #inclu ...
- CSP2019滚粗记
CSP2019滚粗记 从不拖更 Day -inf 考完月考才停课\kel,然后月考又被吊打了. Day -1 和左边的\({\text L}\color{red} {\text{ongge}}\)右边 ...
- leetcode 903. DI序列的有效排列
题目描述: 我们给出 S,一个源于 {'D', 'I'} 的长度为 n 的字符串 .(这些字母代表 “减少” 和 “增加”.)有效排列 是对整数 {0, 1, ..., n} 的一个排列 P[0], ...
- laravel框架的中间件middleware的详解
本篇文章给大家带来的内容是关于laravel框架的中间件middleware的详解,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. laravel中间件是个非常方便的东西,能将一些逻辑 ...
- vue 复制文本到剪切板上
1.下载clipboard.js npm install vue-clipboard2 --save 2.引入,可以在mian.js中全局引入也可以在单个vue中引入 import Clipboard ...
- POJ 1146 ID Codes 用字典序思想生成下一个排列组合
ID Codes Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 7644 Accepted: 4509 Descript ...
- Sitecore 9 介绍
Sitecore 9就在这里.这个最新版本更大,更智能,更易于使用 - 并且更好地帮助您实现业务和数字目标. 现在,Sitecore 9对营销人员和非Sitecore开发人员来说更容易使用.它拥有许多 ...
- 使用python远程连接数据库
根据web连接服务的原理,我们可以通过一台电脑连接我们另一台电脑上的数据库 一.开启数据库的权限1.Mysql:1)修改访问权限首先修改可以访问的ip,把‘localhost’全部修改为‘%’,打开c ...