volatile足以保证数据同步吗
上一节已经了解了java内存模型,JMM为了提高执行性能,引入了工作内存和主存两个概念,在继续讨论之前必须先搞清四种存储介质:寄存器、高级缓存、RAM和ROM。
RAM与ROM大家都比较熟悉了,可以看成是我们经常说的内存与硬盘,寄存器属于处理器里面的一部分,而高级缓存cache是CPU设计者为提高性能引入的一个缓存,也可以说是属于处理器的一部分。在利用CPU进行运算时必定涉及操作数的读取,假如CPU直接读取ROM,那么这个读取速度简直是无法忍受的,于是引入了内存RAM,这样做确实让速度提高了很多,但由于CPU发展十分迅猛而另一方面RAM的发展受到技术及成本的限制发展缓慢,此时产生了一个很难调和的矛盾,CPU运算速度比从RAM读取数据的速度快了几个数量级,木桶原理我们都很熟悉了,桶的容量大小取决于最短的那块,这必将影响处理器的效率,于是又引入了高级缓存,直接在CPU添加了几个级别的缓存,他们的速度虽然无法与寄存器比较,但是速度已经提升很多,基本能跟CPU的计算速度相匹配。总结成一句话就是,为了解决CPU运算速度与读取速度的矛盾,引入了多级存储机制。
如图2-5-2-1所示,机器的四种存储介质是有关系的,一般程序运行时会将ROM相关的程序数据都读进RAM中,而需要运算的数据或运算过程中即将要用到的数据则会被读进高速缓存或寄存器中,假如要进行的运算所需要的所有数据及指令都在寄存器和高速缓存中,则这个运算过程则表现得非常平坦,并不存在性能瓶颈,因为运算速度跟读取速度基本匹配了。读取速度快慢的排序如下:寄存器>cache>RAM>ROM,用一个比较好理解但不完全正确的概念来解释,因为寄存器是离CPU最近的,所以读取最快,高速缓存次之,RAM第三,ROM离得最远,自然速度最慢(当然不能完全用距离来说明这个问题,但用距离是比较好理解的,另外的还因为这些存储介质的硬件设计不同、工作方式不同)。从另一个角度来看,CPU读取数据的顺序是先尝试读寄存器,如果不存在则尝试读高速缓存,如果还不存在则读RAM,最后才是读ROM。一些CPU有三级cache,读取时是一级一级往下直到找到需要的操作数,一般做的比较好的CPU3级缓存已经能让命中率高达95%以上。
图2-5-2-1 四种存储介质
有了上面的知识再往下探索就水到渠成了,如果把Java内存模型与多级存储机制类比将发现为了提高性能java引入了工作内存的概念,提高了线程执行时读取数据的速度,这样就可以把java模型中的主存和工作内存分别于RAM和高速缓存或寄存器对应起来,每条线程的工作内存预先把需要的数据复制到高速缓存或寄存器(但是不保证所有的工作内存的变量副本都是放在高速缓存,也可能在RAM,具体的还要看JVM是如何实现的),这样在多线程并发时性能得到保证。当然寄存器和高速缓存由于成本原因存在容量大小限制的问题,这个也是考验JVM实现的一个难题。
一般引入一种机制解决了一个问题,但同时也会带来另外一个问题,数据同步即是带来的另一个问题,即是否能保证当前运算使用的变量值总是当前时刻最新的值。如果变量值并非最新值,将会导致数据的脏读,最终可能导致计算结果大相径庭。这时可能有人会想起java中有个volatile关键词,毫无疑问它能保证可见性,让每个线程得到的都是主存中最新的变量值,但它就足以保证数据的同步性了吗?举个典型例子,伪代码如下:
private volatile int count=0;
private void increase(){count++}
public static void main(String args){
创建30条线程执行;
每条线程任务都是执行10000次increase()方法;
}
执行完所有线程任务,我们期望的结果会是30*10000,但实际却是一个小于30*10000的数,刚开始看到一定觉得有点奇怪,但仔细一想就清楚了,count++编译后最终并非一个原子操作,它由几个指令一起组合实现。图2-5-2-2能较清晰地说明此点,在Java内存模型中,count++被分割成5个步骤(当然这个并不是确切的指令执行步骤),这5步不具有原子性,假如在完成过程中,其他线程就去读了主存的count变量,那明显导致了一个脏读现象。
图2-5-2-2
导致这个问题的原因其实是因为volatile不具备锁操作,要解决此问题其实不难,就是将这五步变为原子操作,即保证线程一完成之前不能有其他线程读取count变量,对count变量加一个互斥锁即可达到,线程一在执行第①步前对count加锁,其他线程无法对count进行访问,线程一执行完第⑤步后释放锁,此刻开始才允许其他线程获取此变量。
Volatile是一个很容易搞混的关键词,很多经验丰富的开发人员都不能正确使用它,这节从机器结构讲到对应的java内存模型,再引出主存与工作内存之间数据同步的问题,进而更好地解释了volatile的确切含义——它只保证可见性,它不足以保证数据的同步性。
volatile足以保证数据同步吗的更多相关文章
- volatile并不能保证数据同步、只能保证读取到最新主内存数据
在 java 垃圾回收整理一文中,描述了jvm运行时刻内存的分配.其中有一个内存区域是jvm虚拟机栈,每一个线程运行时都有一个线程栈, 线程栈保存了线程运行时候变量值信息.当线程访问某一个对象时候值的 ...
- 基于 MySQL Binlog 的 Elasticsearch 数据同步实践 原
一.背景 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品.订单等数据的多维度检索. 使用 Elasticsearch 存储业务数据可以 ...
- 基于MySQL Binlog的Elasticsearch数据同步实践
一.为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品.订单等数据的多维度检索. 使用 Elasticsearch 存储业务数 ...
- 线程系列07,使用lock语句块或Interlocked类型方法保证自增变量的数据同步
假设多个线程共享一个静态变量,如果让每个线程都执行相同的方法每次让静态变量自增1,这样的做法线程安全吗?能保证自增变量数据同步吗?本篇体验使用lock语句块和Interlocked类型方法保证自增变量 ...
- volatile之一--volatile不能保证原子性
Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和 volatile 关键字机制在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这 ...
- Windows下多线程数据同步互斥的有关知识
对于操作系统而言,在并行程序设计中难免会遇到数据同步和共享的问题,本文针对这个问题,以windows系统为例回顾一下资源同步的相关问题.要点如下: 1.同步和数据共享 数据征用 2.同步原语 ...
- Volatile不保证原子性(二)
Volatile不保证原子性 前言 通过前面对JMM的介绍,我们知道,各个线程对主内存中共享变量的操作都是各个线程各自拷贝到自己的工作内存进行操作后在写回到主内存中的. 这就可能存在一个线程AAA修改 ...
- 使用 volatile 关键字保证变量可见性和禁止指令重排序
volatile 概述 volatile 是 Java 提供的一种轻量级的同步机制.相比于传统的 synchronize,虽然 volatile 能实现的同步性要差一些,但开销更低,因为它不会引起频繁 ...
- [从源码学设计]蚂蚁金服SOFARegistry 之 LocalDataServerChangeEvent及数据同步
[从源码学设计]蚂蚁金服SOFARegistry 之 LocalDataServerChangeEvent及数据同步 目录 [从源码学设计]蚂蚁金服SOFARegistry 之 LocalDataSe ...
随机推荐
- ios开发-指纹识别
最近我们使用支付宝怎么软件的时候,发现可以使用指纹了,看起来是否的高大上.当时苹果推出了相关接口,让程序写起来很简单哈. 在iPhone5s的时候,苹果推出了指纹解锁.但是在ios8.0的时候苹果才推 ...
- [自用]数论和组合计数类数学相关(定理&证明&板子)
0 写在前面 本文受 NaVi_Awson 的启发,甚至一些地方直接引用,在此说明. 1 数论 1.0 gcd 1.0.0 gcd $gcd(a,b) = gcd(b,a\;mod\;b)$ 证明:设 ...
- [HNOI 2014]江南乐
Description 题库链接 给你指定一个数 \(f\) ,并给你 \(T\) 组游戏,每组有 \(n\) 堆石子, \(A,B\) 两人轮流对石子进行操作,每次你可以选择其中任意一堆数量不小于 ...
- [HNOI2016]最小公倍数
题目描述 给定一张N个顶点M条边的无向图(顶点编号为1,2,...,n),每条边上带有权值.所有权值都可以分解成2a∗3b2^a*3^b2a∗3b 的形式. 现在有q个询问,每次询问给定四个参数u.v ...
- 小明搬家_NOI导刊2010提高(05)
题目描述 小明要搬家了,大家都来帮忙. 小明现在住在第N楼,总共K个人要把X个大箱子搬上N楼. 最开始X个箱子都在1楼,但是经过一段混乱的搬运已经乱掉了.最后大家发现这样混乱地搬运过程效率太低了,于是 ...
- 习题9-5 UVA 242
Stamps and Enovelope Size 题意: 给你最多贴S张邮票.有N个邮票集合,每个集合有不同的面值.问哪个集合的最大连续邮资最大,输出最大连续邮资和集合元素. 如果不止一个集合结果相 ...
- 用js来实现那些数据结构11(字典)
我们这篇文章来说说Map这种数据结构如何用js来实现,其实它和集合(Set)极为类似,只不过Map是[键,值]的形式存储元素,通过键来查询值,Map用于保存具有映射关系的数据,Map里保存着两组数据: ...
- SpringMvc+Spring+MyBatis 基于注解整合
最近在给学生们讲Spring+Mybatis整合,根据有的学生反映还是基于注解实现整合便于理解,毕竟在先前的工作中团队里还没有人完全舍弃配置文件进行项目开发,由于这两个原因,我索性参考spring官方 ...
- C语言程序设计第五次作业--循环结构(1)
(一)改错题 输出华氏摄氏温度转换表:输入两个整数lower和upper,输出一张华氏摄氏温度转换表,华氏温度的取值范围是{lower,upper},每次增加2℉.计算公式如下: c = 5×(f-3 ...
- 如何用git命令生成Patch和打Patch
在程序员的日常开发与合作过程中,对于code的生成patch和打patch(应用patch)成为经常需要做的事情.什么是patch?简单来讲,patch中存储的是你对代码的修改,生成patch就是记录 ...