volatile原理和应用场景
volatile是java语言中的一个关键字,常用于并发编程,有两个重要的特点:具有可见性,java虚拟机实现会为其满足Happens before原则;不具备原子性.用法是修饰变量,如:volatile int i.
volatile原理
介绍其可见性先从cpu,cpu缓存和内存的关系入手.
cpu缓存是一种加速手段,cpu查找数据时会先从缓存中查找,如果不存在会从内存中查找,所以如果缓存中数据和内存中数据不一致,cpu处理数据的一致性就无法保证.从机器语言角度来讲,有一些一致性协议来保证缓存一致,但是本文主要从抽象角度解释volatile为何能保证可见性.对于volatile变量的赋值,会刷入主内存,并且通知其他cpu核心,你们缓存中的数据无效了,这样所有cpu核心再想对该volatile变量操作首先会从主内存中重新拉取值.这就保证了对于cpu操作的数据是最新.
但是这并不能保证volatile修饰的变量的原子性.让我们想想一个场景,变量volatile int count存储在内存中,cpu核心1和cpu核心2同时读取该数据,并存入缓存,然后进行count++操作.count++实际可以分解为三步:
int tmp = count;
tmp = count + 1;
count = tmp;
当count = tmp执行结束,cpu会把count刷入内存并通知其他cpu缓存无效,如果两个cpu核心同时将其刷入了内存,通知了缓存无效,那么我们是不是只得到了count = 2,是不是丢失了一个+1的值.所以不要试图用volatile保证多步操作的原子性,原子性可以通过synchronized进行维护.
需要注意一点,long类型和double类型的数据长度是64位的,JVM规范允许对于64位类型数据分开赋值,即高位32位和低位32位可以分开赋值,对于这种情况可以使用volatile修饰保证其赋值是一次完成的.但是!!!虽然JVM是这样规定的,绝大多数虚拟机还是实现了64位数据赋值的原子性,即使不使用volatile关键字进行修饰也不会出现读取到只赋值一半的64位类型数据,所以不必要每个long和double变量之前添加volatile关键字.
感受一下volatile
了解完原理,来通过一段代码感受下volatile.
public class Volatile implements Runnable{
//自增变量i
public /*volatile*/ int i = 0;
@Override
public void run() {
while (true){
i++; //不断自增
}
}
public static void main(String[] args) throws InterruptedException {
Volatile vt = new Volatile();
Watcher watcher = new Watcher();
watcher.v = vt;
Thread t1 = new Thread(vt);
Thread t2 = new Thread(watcher);
t1.start();
t2.start();
Thread.sleep(10);
//打印 i 和 s
System.out.println("Volatile.i = " + vt.i + "\nwatcher.w = " + watcher.monitor);
System.exit(0);
}
}
class Watcher implements Runnable{
public Volatile v;
public int monitor;
@Override
public void run() {
while (true){
monitor = v.i;//不断将v.i的值赋给s
}
}
}
// 这是未加volatile修饰的输出
Volatile.i = 2517483
watcher.w = 1047805
// 打开volatile注释的输出结果
Volatile.i = 332754
watcher.w = 333354
第一个输出中未加volatile修饰的i的值和watcher读取的值相差太远,
第二个输出中相差就不多了.并且i的值比未加volatile关键字的值差很多,说明对volatile变量的赋值消耗会大一些,不过不用在意,我们很少对volatile关键字进行不断自增操作,一般都是作为状态或者保证对象完整性,而且volatile比synchronized轻量太多了,如果只为了保证可见性,volatile一定是最优选.
哪些场景使用volatile
状态变量
由于boolean的赋值是原子性的,所以volatile布尔变量作为多线程停止标志还简单有效的.
class Machine{
volatile boolean stopped = false;
void stop(){stopped = true;}
}
对象完整发布
这里要提到单例对象的双重检查锁,对象完整发布也依赖于happens before原则,有兴趣可以自己去查阅,这个原则是比较啰嗦,可以简单理解为我满足happens before,那么我之前的代码按顺序执行.
public class Singleton {
//单例对象
private static Singleton instance = null;
//私有化构造器,避免外部通过构造器构造对象
private Singleton(){}
//这是静态工厂方法,用来产生对象
public static Singleton getInstance(){
if(instance ==null){
//同步锁防止多次new对象
synchronized (Singleton.class){
//锁内非空判断也是为了防止创建多个对象
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
这是一个会产生bug的双重检查锁代码,instance = new Singleton()并不是一步完成的,他被分为这几步:
1.分配对象空间;
2.初始化对象;
3.设置instance指向刚刚分配的地址。
下面图中,线程A红色先获得锁,B黄色后进入.
这种情况会出现bug,但是由于volatile满足happens before原则,所以会等对象实例化之后再对地址赋值,我们需要将private static Singleton instance = null;改成private static volatile Singleton instance = null;即可.
其实还有几种场景,如果想了解更多建议阅读IBM的技术社区的文章https://www.ibm.com/developerworks/cn/java/j-jtp06197.html
volatile原理和应用场景的更多相关文章
- 【并发编程】Volatile原理和使用场景解析
目录 一个简单列子 Java内存模型 缓存不一致问题 并发编程中的"三性" 使用volatile来解决共享变量可见性 volatile和指令重排(有序性) volatile和原子性 ...
- volatile原理解析
Java并发编程:volatile关键字解析 volatile 有序性.可见性 volatile可以保证一定程度上有序性,即volatile前面的代码先于后面的代码先执行. 但是前.后代码,各自里面的 ...
- Java进阶(七)正确理解Thread Local的原理与适用场景
原创文章,始自发作者个人博客,转载请务必将下面这段话置于文章开头处(保留超链接). 本文转发自技术世界,原文链接 http://www.jasongj.com/java/threadlocal/ Th ...
- ThreadLocal 原理和使用场景分析
ThreadLocal 不知道大家有没有用过,但至少听说过,今天主要记录一下 ThreadLocal 的原理和使用场景. 使用场景 直接定位到 ThreadLocal 的源码,可以看到源码注释中有很清 ...
- 深入解析ThreadLocal 详解、实现原理、使用场景方法以及内存泄漏防范 多线程中篇(十七)
简介 从名称看,ThreadLocal 也就是thread和local的组合,也就是一个thread有一个local的变量副本 ThreadLocal提供了线程的本地副本,也就是说每个线程将会拥有一个 ...
- 蓝牙协议分析(12)_LQ和RSSI的原理及应用场景
在蓝牙协议栈的物理层,有这样两个比较有用的参数:LQI和RSSI.它们都是通过接收端,判断当前无线环境的质量(链路质量),以指导后续的动作.但这两个数值的计算原理和使用场景又有很大的差别. LQI ( ...
- 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?
在日常开发中,尤其是业务开发,少不了利用 Java 对数据库进行基本的增删改查等数据操作,这也是 Java 工程师的必备技能之一.做好数据操作,不仅仅需要对 Java 语言相关框架的掌握,更需要对各种 ...
- [转帖]Docker五种存储驱动原理及应用场景和性能测试对比
Docker五种存储驱动原理及应用场景和性能测试对比 来源:http://dockone.io/article/1513 作者: 陈爱珍 布道师@七牛云 Docker最开始采用AUFS作为文件系统 ...
- laravel基础课程---7、文件处理、闪存、cookie(cookie原理和使用场景)
laravel基础课程---7.文件处理.闪存.cookie(cookie原理和使用场景) 一.总结 一句话总结: 页面请求服务器的时候是把这个页面中所有的cookie都带上了的,cookie里面也存 ...
随机推荐
- Windows7下面手把手教你安装Django - Hongten
我所使用的操作系统是Windows7,内存是2G 在搜索了一些资料发现,对于Django的安装,详细的真的很少,都说的很简化,然而,这篇blog可以手把手教你成功安装Django 对于Django的详 ...
- lvs 四层负载相关
都打开 /etc/sysctl.conf 中的 net.ip4.ip_forward=1.开启路由转发功能. 分发器 : eth0:192.168.1.66 (VIP) eth1:192.168.2. ...
- Swift-技巧(四)设置照片尺寸和格式
摘要 平时实现拍照功能时,都是网上一通搜索,整体复制粘贴,自称无脑实现.但是当要求照片是不同的尺寸和格式( JPEG)时,就费力搞照片.其实在设置拍照时,就可以直接设置照片的尺寸和格式,用直接的方法来 ...
- Linux 守护进程原理及实例(Redis、Nginx)
1. 什么是守护进程 守护进程daemon,是指没有控制终端,运行在后台的进程,通常伴随着系统启动产生,系统关机结束.可以使用命令ps -axj查看系统的守护进程,输出如下所示: 父ID PID 组I ...
- docker中镜像的作用
镜像原理镜像 镜像到底是什么?镜像是一种轻量级.可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码.运行时.库.环境变量和配置文件.1.Uni ...
- 一维前缀和 连续数组和为k
给定一个整数数组和一个整数 k ,请找到该数组中和为 k 的连续子数组的个数. 滑动窗口没办法解决有负数的情况 方法一: 预处理 前缀和 sum_ij = preSum[j] - preSum[i-1 ...
- Java学到什么程度能找到一份还不错的工作
我的读者里有很多 Java 新人,新人是指正在学 Java 的.以及工作时间不长的年轻人,他们经常问我一个问题: Java 学到什么程度才能找到一份还不错的工作? 今天我就从我自己面试新人的角度来回答 ...
- yaml基本用法
简介 YAML 是 "YAML Ain't Markup Language"(YAML 不是一种标记语言)的递归缩写.在开发的这种语言时,YAML 的意思其实是:"Yet ...
- oracle 使用union提示ORA-00907:缺少右括号
在使用union的时候提示:ORA-00907:缺少右括号 此原因是因为在union的左边和右边都有order by,因此需要去掉一边的order by,保留一个即可. 例如: select * fr ...
- C++概述及知识点总结
经过一段时间的学习,以前从没有接触过C++这个高逼格的语言的小白,逐渐对C++有了更深的了解和认识,C++是c语言的升级版,Bjarne Stroustrup在剑桥大学计算机中心工作.他使用过Simu ...