Java内存模型-volatile内存语义
章节目录
- 1.volatile 的特性
- 为什么volatile修饰变量的写操作不是原子性的?
- 2.volatile 写-读建立的 happens-before 关系
- 3.volatile 写-读的内存语义
1.volatile 的特性
首先应该明确的一点是:当声明共享变量为volatile后,对这个变量的读/写(分为单元素读写,与复合写操作)。不同的读写模式下,volatile变量对写操作的原子性体现是不一样的。
理解volatile特性的一个好办法是把对volatile变量的单个读/写,看成是同一个锁对这些单个读/写操作做了同步,示例代码如下所示:
class VolatileFeaturesExample { volatile long vl = 0L; public void set(long l){ vl = l; } public void getAndIncrement() { vl++; } public long get(){ return vl; } }
假设有多个线程调用上述程序中的3个方法,这个程序语义和下面的程序语义等价
class VolatileFeaturesExample { long vl = 0L; public synchronized void set(long l){ vl = l; } public void getAndIncrement() { long temp = get(); temp +=1L; set(temp); } public syntronized long get(){ return vl; } }
解释
- 如这两个程序所示,一个volatile 变量的单个读/写操作,与一个普通变量的读/写操作都是使用同一个锁来同步,他们之间的执行效果相同。
- 锁的happens-before规则(保证前一个操作结果对后一个操作立即可见)保证释放锁和获取锁的两个线程之间的内存可见性。这很好的解释了对于一个volatile变量的读,总能看到任意线程对这个volatile变量最后的写入。
- 锁的语义决定了临界代码区的执行具有原子性。这意味着,即使是64位的long型和double型变量,只要它是volatile变量,对该变量的单个读/写就具有原子性,如果是多个volatile操作,或这是volatile++这种复合操作,这些操作在整体上是不具有原子性的。
volatile 自身特性:
1.内存可见性:对一个volatile变量的读,总能看到任意线程对这个volatile变量 最后的写入(内存可见性保证) 2.原子性:对任意单个volatile变量的单独的读写操作,都具有原子性。但对于 volatile++这种复合操作不具有原子性。
2.volatile 写-读建立的 happens-before 关系
JMM基于共享内存模型实现线程之间的通信
从jdk1.5 开始 volatile 变量的写-读可以实现线程之间的通信。
volatile & synchronized内存语义
从内存语义来说,volatile的写-读与锁的释放-获取有相同的内存效果:volatile 写和锁释放有相同的内存语义;volatile读与锁的获取有相同的内存语义。
下面为volatile变量示例代码:
class VolatileExample { int a = 0;//其实是线程共享变量 volatile boolean flag = false; public void writer(){ a = 1; //1 flag = true; //2 } public void reader(){ if(flage){ //3 int i = a; //4 ...... } } }
假设线程A 执行 writer() 方法之后,线程B 执行reader() 方法。根据happens-before 规则,这个过程建立的happens-before规则可以分为三类:
1.根据程序次序规则,1 happens before 2;3 happens before 4.
2.根据volatile规则,2 happens-before 3
3.根据happens-before c传递性规则 1 happens-before 4
图形化形势如下图所示:
A线程在写一个volatile变量后,B线程读同一个volatile变量。A线程在写volatile之前所有的可见共享变量,在B线程读到同一个volatile变量之后,将立即变得对B线程可见。
4. volatile 写-读的内存语义
** volatile 写内存语义 **
当写一个volatile变量时,JMM会把该线程对应的本地内存的变量中的变量值强制刷新到主内存。
volatile 读的内存语义
当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。
volatile内存语义 总结
- 线程A 写一个volatile 变量,实质上是线程A 向接下来读取这个volatile变量的某个线程发出了(其对共享变量所做修改的)消息。
- 线程B 读一个volatile 变量,实质上是线程B 接收了之前某个线程发出的(在写这个volatile变量之前对共享变量所做修改)消息。
- 线程A 写一个volatile 变量,随后线程B读这个volatile变量。这个过程实质上是线程A通过主内存向线程B发送消息。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
window的特性
参考文献:https://xz.aliyun.com/t/2318https://www.jianshu.com/p/96cf08d569bcWinAPI与通配符 在Windows环境下使用PHP时,PHP中的部分函数会调用2个底层Windows API函数FindFirstFileExW(),FindFirstFile() 这两个函数对< > "三个字符做了特别的对待和处理: 大于号 > 等价于 通配符 ? 0次或1次 小于号 < 等价于 通配符 * 0次或多次 双引号 " 等价于 通配符 . 匹配除换行符(\n, \r)之外的任意单个字符 也就是说,在Windows下的PHP的某些函数中,我们可以使用< > "来匹配一些文件名/目录名,将文件名不可知部分之后的字符用"<",">"代替,只用一个"<"或">"只能代表一个字符,若文件名很长需要用"<<"才行。 这里的部分函数包括但不限于: include() 包含文件 include_once() 包含文件 require() 包含文件 require_once(...
- 下一篇
【Python标准库:re】如何在Python中使用正则表达式
Python的正则表达式 Python通过导入标准库re实现正则表达式(regular expression),Python的正则表达式引擎和Perl一样,并且兼容Perl流派的元字符。 元字符 Python支持的元字符很多,一种是比较常见,我之前也就只会用这些 .表示任意一个字符,默认不匹配换行符,制表符 |表示或,ca|bd会匹配ca或bd,而不是cab, cbd, 如果想要匹配后者,则需要用到()进行分组 ^,$表示位置符号,行首和行尾 如^ab$匹配ab, 不匹配eab, abe,aeb 量词,表示重复数,*任意多次, +一次以上, ?0次或一次, {m,n}m~n次, {m}重复m次,{m,}重复大于m次 在上述量词后接?, 就从贪婪模式变为非贪婪模式。举个例子,对于abbbbbb这个字符串,ab*和ab*?的结果不同,前者匹配abbbbbb,后者匹配a,也就是贪婪模式尽可能多匹配。 [...]表示多选项,比如a[bc]就可以匹配ab,ac, 如果是[a-z]那么表示从a到z范围. 所有元字符在[]中都会被认为是普通字符。所有元字符在[] (...)表示捕获型分组,被(......
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Hadoop3单机部署,实现最简伪集群
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- CentOS7安装Docker,走上虚拟化容器引擎之路
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Docker使用Oracle官方镜像安装(12C,18C,19C)