你真的了解volatile吗,关于volatile的那些事
很早就接触了volatile,但是并没有特别深入的去研究她,只有一个朦胧的概念,就是觉得
用她来解决可见性的,但可见性又是什么呢?
最近经过查阅各种资料,并结合自己的思考和实践,对volatile有了比较深刻的认识,
在此总结并分享给大家。
可见性
如何理解可见性,还是来看个会出现死循环的例子:
(注意:运行时请加上jvm参数:-server,while循环内不要有标准输出):
这是为什么呢?先来看看java的内存模型,如下图:
java内存分为工作内存和主存
工作内存:即java线程的本地内存,是单独给某个线程分配的,存储局部变量等,同时也会复制主存的共享变量作为本地
的副本,目的是为了减少和主存通信的频率,提高效率。
主存:存储类成员变量等
可见性是指的是线程访问变量是否是最新值。
局部变量不存在可见性问题,而共享内存就会有可见性问题,
因为本地线程在创建的时候,会从主存中读取一个共享变量的副本,且修改也是修改副本,
且并不是立即刷新到主存中去,那么其他线程并不会马上共享变量的修改。
因此,线程B修改共享变量后,线程A并不会马上知晓,就会出现上述死循环的问题。
解决共享变量可见性问题,需要用volatile关键字修饰。
如下图代码就不会出现死循环:
那么为什么能解决死循环的问题呢?
可见性的特性总结为以下2点:
1. 对volatile变量的写会立即刷新到主存
2. 对volatile变量的读会读主存中的新值
可以用如下图更清晰的描述:

如此一来,就不会出现死循环了。
为了能更深刻的理解volatile的语义,我们来看下面的时序图,回答这2个问题:

问题1:t2时刻,如果线程A读取running变量,会读取到false,还是等待线程B执行完呢?
答案是否定的,volatile并没有锁的特性。
问题2:t4时刻,线程A是否一定能读取到线程B修改后的最新值
答案是肯定的,线程A会从重新从主存中读取running的最新值。
还有一种办法也可以解决死循环的问题:

虽然running变量上没有volatile关键字修饰,但是读和写running都是同步方法
同步块存在如下语义:
1.进入同步块,访问共享变量会去读取主存
2.退出同步块,本地内存对共享变量的修改会立即刷新到主存
因此上述代码不会出现死循环。
volatile变量的原子性
我看了很多文章,有些文章甚至是出版的书籍都说volatile不是原子的,
他们举的例子是i++操作,i++本身不是原子操作,是读并写,我这里要讲的原子性
指的是写操作,原子性的特别总结为2点:
1. 对一个volatile变量的写操作,只有所有步骤完成,才能被其它线程读取到。
2. 多个线程对volatile变量的写操作本质上是有先后顺序的。也就是说并发写没有问题。
这样说也许读者感觉不到和非volatile变量有什么区别,我来举个例子:
//线程1初始化User
User user;
user = new User();
//线程2读取user
if(user!=null){
user.getName();
}
在多线程并发环境下,线程2读取到的user可能未初始化完成
具体来看User user = new User的语义:
1:分配对象的内存空间
2:初始化对线
3:设置user指向刚分配的内存地址
步骤2和步骤3可能会被重排序,流程变为
1->3->2
这些线程1在执行完第3步而还没来得及执行完第2步的时候,如果内存刷新到了主存,
那么线程2将得到一个未初始化完成的对象。因此如果将user声明为volatile的,那么步骤2,3
将不会被重排序。
下面我们来看一个具体案例,一个基于双重检查的懒加载的单例模式实现:

这个单例模式看起来很完美,如果instance为空,则加锁,只有一个线程进入同步块
完成对象的初始化,然后instance不为空,那么后续的所有线程获取instance都不用加锁,
从而提升了性能。
但是我们刚才讲了对象赋值操作步骤可能会存在重排序,
即当前线程的步骤4执行到一半,其它线程如果进来执行到步骤1,instance已经不为null,
因此将会读取到一个没有初始化完成的对象。
但如果将instance用volatile来修饰,就完全不一样了,对instance的写入操作将会变成一个原子
操作,没有初始化完,就不会被刷新到主存中。
修改后的单例模式代码如下:

对volatile理解的误区

如果i++的操作是线程安全的,那么预期结果应该是i=20000
然而运行的结果是:11349
说明i++存在并发问题
i++语义是i=i+1
分为2个步骤
步骤1:读取i=0
步骤2:计算i+1=1,并重新赋值给i
那么可能存在2个线程同时读取到i=0,并计算出结果i=1然后赋值给i
那么就得不到预期结果i=2。
这个问题说明了2个问题:
1.i++这种操作不是原子操作
2.volatile 并不会有锁的特性
你真的了解volatile吗,关于volatile的那些事的更多相关文章
- [CareerCup] 13.5 Volatile Keyword 关键字volatile
13.5 What is the significance of the keyword "volatile" in C 这道题考察我们对于关键字volatile的理解,顾名思义, ...
- java 用volatile和不用volatile的区别
在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写.这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值 ...
- 多线程的指令重排问题:as-if-serial语义,happens-before语义;volatile关键字,volatile和synchronized的区别
一.指令重排问题 你写的代码有可能,根本没有按照你期望的顺序执行,因为编译器和 CPU 会尝试指令重排来让代码运行更高效,这就是指令重排. 1.1 虚拟机层面 我们都知道CPU执行指令的时候,访问内存 ...
- 【C# 线程】 volatile 关键字和Volatile类、Thread.VolatileRead|Thread.VolatileWrite 详细 完整
overview 同步基元分为用户模式和内核模式 用户模式:Iterlocked.Exchange(互锁).SpinLocked(自旋锁).易变构造(volatile关键字.volatile类.Thr ...
- volatile和不加volatile的区别
http://blog.chinaunix.net/uid-25100840-id-3067899.html 对于一个地址,如果加上了volatile参数,这个地址也就不会经过编译器优化,也就是说这个 ...
- synchronized 与 volatile 区别 还有 volatile 的含义
熟悉并发的同学一定知道在java中处理并发主要有两种方式: 1,synchronized关键字,这个大家应当都各种面试和笔试中经常遇到. 2,volatile修饰符的使用,相信这个修饰符大家平时在项目 ...
- 你真的了解volatile关键字吗?
volatile关键字经常在并发编程中使用,其特性是保证可见性以及有序性,但是关于volatile的使用仍然要小心,这需要明白volatile关键字的特性及实现的原理,这也是本篇文章的主要内容. 一. ...
- 你真的了解 volatile 关键字吗?
今天,让我们一起来探讨 Java 并发编程中的知识点:volatile 关键字 本文主要从以下三点讲解 volatile 关键字: volatile 关键字是什么? volatile 关键字能解决什么 ...
- [Java并发编程(三)] Java volatile 关键字介绍
[Java并发编程(三)] Java volatile 关键字介绍 摘要 Java volatile 关键字是用来标记 Java 变量,并表示变量 "存储于主内存中" .更准确的说 ...
- 深入理解volatile
volatile知识点 --------------------------------------------------------------------------- 1.volatile关键 ...
随机推荐
- Sql server2014 内存优化表 本地编译存储过程
参考文献:http://www.infoq.com/cn/news/2013/09/Compiled-Queries http://www.bianceng.cn/database/SQLServer ...
- Recurrent Neural Network系列3--理解RNN的BPTT算法和梯度消失
作者:zhbzz2007 出处:http://www.cnblogs.com/zhbzz2007 欢迎转载,也请保留这段声明.谢谢! 这是RNN教程的第三部分. 在前面的教程中,我们从头实现了一个循环 ...
- JS中常见排序算法详解
本文将详细介绍在JavaScript中算法的用法,配合动图生动形象的让你以最快的方法学习算法的原理以及在需求场景中的用途. 有句话怎么说来着: 雷锋推倒雷峰塔,Java implements Java ...
- BZOJ 1497: [NOI2006]最大获利(最大权闭合子图)
1497: [NOI2006]最大获利 Time Limit: 5 Sec Memory Limit: 64 MB Description 新的技术正冲击着手机通讯市场,对于各大运营商来说,这既是机 ...
- 学习笔记--jQuery基础
学习笔记也算总结知识点,有些示例可能没有具体到元素,直接给出的语法.大家有基本理解在看可能会好些. jQuery使用前提,需要安装jQuery库 jQuery 库是一个 JavaScript 文件,您 ...
- SpringMVC总结的部分教程及使用方法
注:本文只用注解来实现 SpringMVC各种流程图流程图(其他的各种流程图)jsp.xml.action彼此之间的关系,都如何使用spring-mvc.xml如何配置,放在哪里?action中如何转 ...
- Spring之IOC讲解
一.SpringIOC的好处: ioc的思想最核心的地方在于,资源不由使用资源的双方管理,而由不使用资源的第三方管理,这可以带来很多好处. 1.资源集中管理,实现资源的可配置和易管理. 2.降低了使用 ...
- ASP.NET应用程序的文件类型及文件夹列表
文件类型: 1. *.aspx文件:这类文件是ASP.NET Web页面,它包括用户接口和隐藏代码. 2. *.ascx文件:这类文件是用户控件.用户控件同Web页面非常相似,但用户不能直接访问用户控 ...
- Java(多态练习 instanceof)
/* 题目: (多态,instanceof)有如下代码 class Animal { private String name; // 1 } class Dog extends Animal { // ...
- 前端总结·基础篇·CSS(一)布局
目录 这是<前端总结·基础篇·CSS>系列的第一篇,主要总结一下布局的基础知识. 一.显示(display) 1.1 盒模型(box-model) 1.2 行内元素(inline) &am ...