Java并发——volatile关键字
什么是内存可见性?
这里就要提一下JMM(Java内存模型)。当线程在运行的时候,并不是直接直接修改电脑主内存中的变量的值。线程间通讯也不是直接把一个线程的变量的值传给另一个线程,让其刷新变量。下面是一副抽象的结构图。
线程A要想和线程B通信,其实是通过改变主内存中的共享变量的值。具体的工作原理就是,线程A不能直接修改主内存中的值,而是在主内存和线程A中需要一个缓存区(每个线程都有自己的一个缓冲区),再将共享变量的副本拷入缓冲区,在缓冲区里修改完之后再通过一些指令将副本改变的值刷新进主内存。这里刷新就会出现很多问题,有可能线程A修改了共享变量的值没有刷新进去,当B需要使用共享变量的时候就会用旧值。所以这就是多线程存在一个比较大的问题。前面介绍的synchronized关键字,以及现在介绍的volatile,后面会介绍的CAS,其实都会解决这个内存可见性的问题。不过实现原理不同。要保证内存可见性也就是每一个线程修改一次共享变量的值,都需要让主内存中的变量刷新,并且让其他线程也刷新共享变量副本。
volatile如何实现内存可见性?
其实volatile的可见性底层原理主要是内存屏障和禁止重排序。内存屏障是在volatile变量读操作之前加入load指令,从主内存中读取最新的共享变量,而不是使用之前的副本值,同样写的时候也是加入store指令,将本地内存中的共享变量值强制刷新到主内存中。这样就保证了每个线程拿到的volatile变量是最新的值。下面是两个示意图
StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能
LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。也就是
LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
上面四个屏障不但会强制变量刷新,而且会防止指令之间的重排序(JVM对于没有数据依赖关系的指令会进行重排序,指令重排序也会导致变量的值读取是错误的)。volatile的底层就是这样实现,较synchronized更为轻量级,毕竟synchronized是用来修饰方法和代码块的,而volatile保证的只是一个变量,所以更为轻量级。
如何优化volatile关键字?
我们先来弄清楚对于英特尔酷睿i7、酷睿、Atom和 NetBurst,以及Core Solo和Pentium M处理器的L1、L2或L3缓存的高速缓存行是64个字节宽,不支持部分填充缓存行。要想优化volatile变量运行速度,只需要将变量追加到64个字节
也就是如果一个volatile变量存入高速缓存且不足64个字节长度,这时候可能在同一个缓存行中就会再存入另一个volatile变量,而由于缓存一致性机制,当处理第一个volatile变量的时候,整个缓存行是锁定的,这时候第二个volatile变量或者缓存行其他变量需要被其他处理器操作就必须等到第一个volatile变量操作完才能进行。如果这时volatile变量是64个字节独占一个缓存行的时候,那么就不会有上面的影响发生。
那么什么时候我们都需要追加64个字节吗?有下面两种情况不需要追加
1、缓存行非64字节宽的处理器。如P6系列和奔腾处理器,它们的L1和L2高速缓存行是32个字节宽。
2、共享变量不被频繁地写。可以看出追加字节的方式是需要性能消耗的,如果共享变量不被频繁地写的话,锁定的概率小,不需要追加字节。
总结
volatile是修饰变量的一个关键字,写在基本数据类型之前。用来保证这个变量在多线程的环境下具有可见性。是通过内存屏障和禁止指令重排序来实现的,但是不具有原子性。CAS和synchronized才会保证原子性。
Java并发——volatile关键字的更多相关文章
- Java 并发 —— volatile 关键字
volatile 修饰变量等于向编译器传达如下两层含义: 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的. 禁止进行指令重排序. volat ...
- Java并发--volatile关键字
一.volatile的实现原理 synchronized是阻塞式同步,在线程竞争激烈的情况下会升级为重量级锁,而volatile就可以说是JVM提供的最轻量级的同步机制.JMM告诉我们,各个线程会将共 ...
- Java并发——volatile关键字的使用
volatile关键字的使用volatile关键字原理适合使用volatile关键字的情况当且仅当满足以下所有条件时,才==应该==使用volatile关键字:volatile关键字的作用volati ...
- 【转】java中volatile关键字的含义
java中volatile关键字的含义 在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉. Java语言 ...
- 转:java中volatile关键字的含义
转:java中volatile关键字的含义 在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉. Java语言 ...
- java 并发——volatile
java 并发--volatile 介绍 维基百科: volatile 是一个类型修饰符(type specifier).volatile 的作用是确保本条指令不会因编译器的优化而省略,且要求每次直接 ...
- Java并发-volatile的原理及用法
Java并发-volatile的原理及用法 volatile属性:可见性.保证有序性.不保证原子性.一.volatile可见性 在Java的内存中所有的变量都存在主内存中,每个线程有单独CPU缓存内存 ...
- Java并发——volatile的原理
111 Java并发——volatile的原理
- Java中Volatile关键字详解 (转自郑州的文武)
java中volatile关键字的含义:http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html 一.基本概念 先补充一下概念:J ...
随机推荐
- Tiny4412之重力感应器驱动
一:Tiny4412 裸板重力感应驱动编写 整个编写过程大概分为如下几步: (1)在底板上找到重力感应器的所在位置,找到芯片型号(我用的板子上重力感应器芯片型号为:MMA7660FC) (2)通过型号 ...
- .net 工程中引用出现感叹号
在工程中引用出现感叹号,有两个原因 原因1: 这是由于之前引用的Dll文件不见了. 解决方案: 右键有感叹号的项,然后选择 “属性” 里边有一个路径属性 这个路径就是之前这个Dll文件的路径,现在这 ...
- html5中让页面缩放的4种方法
1.viewport 这种方法,不是所有的浏览器都兼容<meta name="viewport" content="width=640,minimum-scale= ...
- awk高级玩法
1. 程序元素 一个awk 程序是一对以模式(pattern) 与大括号框起来的操作(action) 组合而成的,或许,还会加上实现操作细节的函数(function ) .针对每个匹配于输人数据的模式 ...
- netty源码分析之揭开reactor线程的面纱(一)
netty最核心的就是reactor线程,对应项目中使用广泛的NioEventLoop,那么NioEventLoop里面到底在干些什么事?netty是如何保证事件循环的高效轮询和任务的及时执行?又是如 ...
- Tensorflow学习-数据读取
Tensorflow数据读取方式主要包括以下三种 Preloaded data:预加载数据 Feeding: 通过Python代码读取或者产生数据,然后给后端 Reading from file: 通 ...
- bzoj5250 [2018多省省队联测]秘密袭击
博主蒟蒻,目前还不会动态dp,所以下面说的是一个并不优秀的暴力,我会补的! 我们考虑按权值从大到小依次点亮每个点,相同权值可以同时点亮,每次点亮后,我们进行一次树形背包. 处理出$f[i][j]$表示 ...
- BZOJ_2161_布娃娃_权值线段树
BZOJ_2161_布娃娃_权值线段树 Description 小时候的雨荨非常听话,是父母眼中的好孩子.在学校是老师的左右手,同学的好榜样.后来她成为艾利斯顿第二 代考神,这和小时候培养的良好素质是 ...
- golang sync/atomic
刚刚学习golang原子操作处理的时候发现github上面一个比较不错的golang学习项目 附上链接:https://github.com/polaris1119/The-Golang-Standa ...
- python selenium中Excel数据维护(二)
接着python里面的xlrd模块详解(一)中我们我们来举一个实例: 我们来举一个从Excel中读取账号和密码的例子并调用: ♦1.制作Excel我们要对以上输入的用户名和密码进行参数化,使得这些数据 ...