Java并发(六):volatile的实现原理
synchronized是一个重量级的锁,volatile通常被比喻成轻量级的synchronized
volatile是一个变量修饰符,只能用来修饰变量。
volatile写:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存。
volatile读:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。
volatile实现原理
1)JMM把内存屏障指令分为下列四类:

StoreLoad Barriers是一个“全能型”的屏障,它同时具有其他三个屏障的效果。现代的多处理器大都支持该屏障(其他类型的屏障不一定被所有处理器支持)。执行该屏障开销会很昂贵,因为当前处理器通常要把写缓冲区中的数据全部刷新到内存中(buffer fully flush)。
Store:数据对其他处理器可见(即:刷新到内存)
Load:让缓存中的数据失效,重新从主内存加载数据
2)JMM针对编译器制定的volatile重排序规则表
| 是否能重排序 | 第二个操作 | ||
| 第一个操作 | 普通读/写 | volatile读 | volatile写 |
| 普通读/写 | NO | ||
| volatile读 | NO | NO | NO |
| volatile写 | NO | NO | |
举例来说,第三行最后一个单元格的意思是:在程序顺序中,当第一个操作为普通变量的读或写时,如果第二个操作为volatile写,则编译器不能重排序这两个操作。
从上表我们可以看出:
- 当第二个操作是volatile写时,不管第一个操作是什么,都不能重排序。这个规则确保volatile写之前的操作不会被编译器重排序到volatile写之后。
- 当第一个操作是volatile读时,不管第二个操作是什么,都不能重排序。这个规则确保volatile读之后的操作不会被编译器重排序到volatile读之前。
- 当第一个操作是volatile写,第二个操作是volatile读时,不能重排序。
JMM内存屏障插入策略(编译器可以根据具体情况省略不必要的屏障):
- 在每个volatile写操作的前面插入一个StoreStore屏障。
- 对于这样的语句Store1
StoreStoreStore2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见
- 对于这样的语句Store1
- 在每个volatile写操作的后面插入一个StoreLoad屏障。
- 对于这样的语句Store1
StoreLoadLoad2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见
- 对于这样的语句Store1
- 在每个volatile读操作的后面插入一个LoadLoad屏障。
- 对于这样的语句Load1
LoadLoadLoad2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕
- 对于这样的语句Load1
- 在每个volatile读操作的后面插入一个LoadStore屏障。
- 对于这样的语句Load1
LoadStoreStore2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕
- 对于这样的语句Load1
volatile保证可见性
volatile修饰的变量写之后将本地内存刷新到主内存,保证了可见性
volatile保证有序性
volatile变量读写前后插入内存屏障以避免重排序,保证了有序性
volatile不保证原子性
volatile不是锁,与原子性无关
要我说,由于CPU按照时间片来进行线程调度的,只要是包含多个步骤的操作的执行,天然就是无法保证原子性的。因为这种线程执行,又不像数据库一样可以回滚。如果一个线程要执行的步骤有5步,执行完3步就失去了CPU了,失去后就可能再也不会被调度,这怎么可能保证原子性呢。
为什么synchronized可以保证原子性 ,因为被synchronized修饰的代码片段,在进入之前加了锁,只要他没执行完,其他线程是无法获得锁执行这段代码片段的,就可以保证他内部的代码可以全部被执行。进而保证原子性。
(摘自http://www.hollischuang.com/archives/2673)
volatile不保证原子性的例子:
/**
* 创建10个线程,然后分别执行1000次i++操作。目的是程序输出结果10000
* 但是,多次执行的结果都小于10000。这其实就是volatile无法满足原子性的原因。
*/
public class Test {
public volatile int inc = 0; public void increase() {
inc++;
} public static void main(String[] args) {
final Test test = new Test();
for (int i = 0; i < 10; i++) {
new Thread() {
public void run() {
for (int j = 0; j < 1000; j++)
test.increase();
};
}.start();
} while (Thread.activeCount() > 1)
// 保证前面的线程都执行完
Thread.yield();
System.out.println(test.inc);
}
}
volatile的内存屏障插入策略非常保守,其实在实际中,只要不改变volatile写-读得内存语义,编译器可以根据具体情况优化,省略不必要的屏障。参考:【死磕Java并发】—–Java内存模型之分析volatile
参考资料
Java并发(六):volatile的实现原理的更多相关文章
- 【java并发编程艺术学习】(三)第二章 java并发机制的底层实现原理 学习记录(一) volatile
章节介绍 这一章节主要学习java并发机制的底层实现原理.主要学习volatile.synchronized和原子操作的实现原理.Java中的大部分容器和框架都依赖于此. Java代码 ==经过编译= ...
- Java并发机制的底层实现原理之volatile应用,初学者误看!
volatile的介绍: Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现 ...
- 《Java并发编程的艺术》Java并发机制的底层实现原理(二)
Java并发机制的底层实现原理 1.volatile volatile相当于轻量级的synchronized,在并发编程中保证数据的可见性,使用 valotile 修饰的变量,其内存模型会增加一个 L ...
- Java 并发系列之二:java 并发机制的底层实现原理
1. 处理器实现原子操作 2. volatile /** 补充: 主要作用:内存可见性,是变量在多个线程中可见,修饰变量,解决一写多读的问题. 轻量级的synchronized,不会造成阻塞.性能比s ...
- Java并发关键字Volatile 详解
Java并发关键字Volatile 详解 问题引出: 1.Volatile是什么? 2.Volatile有哪些特性? 3.Volatile每个特性的底层实现原理是什么? 相关内容补充: 缓存一致性协议 ...
- Java 并发 关键字volatile
Java 并发 关键字volatile @author ixenos volatile只是保证了共享变量的可见性,不保证同步操作的原子性 同步块 和 volatile 关键字机制 synchroniz ...
- java并发系列(六)-----Java并发:volatile关键字解析
在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保证可见性 ...
- Java 并发:volatile 关键字解析
摘要: 在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保 ...
- 面渣逆袭:Java并发六十问,快来看看你会多少道!
大家好,我是老三,面渣逆袭 继续,这节我们来盘一盘另一个面试必问知识点--Java并发. 这篇文章有点长,四万字,图文详解六十道Java并发面试题.人已经肝麻了,大家可以点赞.收藏慢慢看!扶我起来,我 ...
- (第二章)Java并发机制的底层实现原理
一.概述 Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现和CPU的指令. ...
随机推荐
- http状态响应码对照表
1xx - 信息提示 这些状态代码表示临时的响应.客户端在收到常规响应之前,应准备接收一个或多个 1xx 响应. ·0 - 本地响应成功. · 100 - Continue 初始的请求已 ...
- esp8266 IOT Demo 固件刷写记录
将编译好的固件按照下面地址刷写到esp8266 出现下面错误是因为刷写的设置不对,按照图上设置: load 0x40100000, len 26828, room 16 tail 12chksum 0 ...
- 【Python学习笔记】colormap的参数及其对应的色条
- Java基础 变量和数据类型及相关操作
Java基本语法: 1):Java语言严格区分大小写,好比main和Main是完全不同的概念. 2):一个Java源文件里可以定义多个Java类,但其中最多只能有一个类被定义成public类.若源文件 ...
- 微信支付:curl 出错,错误码: 60
在测试微信支付 - 模式二 - 扫码支付时,遇到如下错误: curl出错,错误码:60 这是因为在 WxPay.Api.php 文件中做了严格的 ssl 证书校验: curl_setopt($ch,C ...
- mysql utf8改utf8mb4
由于需要用到utf8mb4,之前是utf8现在给改成utf8mb4 查看当前环境 SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\_%' ...
- Phoenix批量修改数据
很简单的一个东西,查了挺久的,浪费了很多的时间 直接用Upsert Into Select就可以了 例:把tables表中cloumn2列等于bbb的都改成aaa Upsert Into Table ...
- 解析jsp的 tomcat 、resin
一.tomcat 1. 安装JDK [root@localhost ~]# cd /usr/local/src/ [root@localhost src]# wget http://www.lishi ...
- Mysql 数据库学习笔记04 函数
一.创建自定义函数 * 使用自定义函数,可以返回字符串.整型.实数或者其他类型: create [aggregate] function 名称 (参数列表) return type begin //函 ...
- Leetcode 之Balanced Binary Tree(49)
用递归的方式来做,左右两棵子树的高度差不超过1.分成两部分,一部分递归得到树的高度,一部分递归检查左右子树是否是平衡二叉树. int getHeight(TreeNode *root) { ; ; } ...