摘要:如何拥有可见性先介绍一下内存模型中定义的种工作内存与主内存之间的原子操作锁定作用于主内存的变量,把一个变量标识为一条线程独占的状态。并没有达到期望的。
前言
要学习好Java的多线程,就一定得对volatile关键字的作用机制了熟于胸。最近博主看了大量关于volatile的相关博客,对其有了一点初步的理解和认识,下面通过自己的话叙述整理一遍。
有什么用?volatile主要对所修饰的变量提供两个功能
可见性
防止指令重排序
本篇博客主要对volatile可见性进行探讨,以后发表关于指令重排序的博文。
一图胜千言
上图已经把JAVA内存模型(JMM)展示得很详细了,简单概括一下
每个Thread有一个属于自己的工作内存(可以理解为每个厨师有一个属于自己的铁锅)
所有Thread共用一个主内存(餐厅所有的厨师共用同一个冰箱)
每个Thread操作数据之前都会去主内存中获取数据(厨师炒菜之前都要去冰箱里拿食材)
Thread:厨师
工作内存:铁锅
store&load:放熟食,取食材
主内存:冰箱
读者可思考以下情景:
餐厅来了一位顾客点了一份红烧肉,此时有两位大厨(假设大厨之间互不通信),由于互不通信,所以两位大厨都打开冰箱取出食材开始炒菜。
最后炒出了两份红烧肉,顾客只要一份。为什么会造成这种结果?
将此情景放在JAVA中即是:
线程A从主内存中取了一个变量到工作内存中,操作完毕后没有及时放回主内存中,于是线程B去取这个变量已经过期了,取的是线程A操作之前的变量。
先介绍一下Java内存模型中定义的8种工作内存与主内存之间的原子操作
lock( 锁定 ):作用于主内存的变量,把一个变量标识为一条线程独占的状态。
unlock(解锁):作用于主内存的变量,把一个处于锁定的变量释放出来,释放变量才可以被其他线程锁定。
read(读取):作用于主内存的变量,把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。
load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
use(使用):作用于工作内存种的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。
assign(赋值):作用于工作内存中的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用
write(写入):作用于主内存的变量,它把store操作从工作内存中得到的值放入主内存的变量中。
当线程1对主内存对象发起read操作到write操作套流程的时间里,线程2随时都有可能对这个主内存对象发起第二套操作
有什么危害呢?
假设主内存中有一个
int a=0;
线程1和线程2分别执行一次,理想状态下最终a的值为2.
a++;
线程1在执行了assign操作之后变量a的真实值已经从0变成了1,但是这个过程发生在工作内存中对其他线程不可见,若线程2此时对变量a的操作,读取到的值仍然为0,因为没有可见性,线程2的操作也仅仅是重复了线程1的操作,再次让a从0变成了1。并没有达到期望的a=2。
volatile变量对对象的操作更严格:
use之前不能被read&load
assign之后必须紧跟store&write
也就是说 read-load-use 和 assign-store-write成为了两个不可分割的原子操作
尽管这时候在use和assign之间依然有一段真空期,有可能变量会被其他线程读取,但是无论在哪一个时间点主内存的变量和任一工作内存的变量的值都是相等的。这个特性就导致了volatile变量不适合参与到依赖当前值的运算,如自增。
那么依靠可见性的特点volatile可以用在哪些地方呢?
《Java虚拟机》提到:
运算结果并不依赖变量的当前值(即结果对产生中间结果不依赖),或者能够确保只有单一的线程修改变量的值
通常volatile用做保存某个状态的boolean值。
部分参考自
volatile变量与普通变量的区别
<<深入理解Java虚拟机 高级特性与最佳实践>>
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/69423.html
摘要:假设不发生编译器重排和指令重排,线程修改了的值,但是修改以后,的值可能还没有写回到主存中,那么线程得到就是很自然的事了。同理,线程对于的赋值操作也可能没有及时刷新到主存中。线程的最后操作与线程发现线程已经结束同步。 很久没更新文章了,对隔三差五过来刷更新的读者说声抱歉。 关于 Java 并发也算是写了好几篇文章了,本文将介绍一些比较基础的内容,注意,阅读本文需要一定的并发基础。 本文的...
摘要:是需要我们去处理很多事情,为了防止多线程给我们带来的安全和性能的问题下面就来简单总结一下我们需要哪些知识点来解决多线程遇到的问题。 前言 不小心就鸽了几天没有更新了,这个星期回家咯。在学校的日子要努力一点才行! 只有光头才能变强 回顾前面: 多线程三分钟就可以入个门了! Thread源码剖析 本文章的知识主要参考《Java并发编程实战》这本书的前4章,这本书的前4章都是讲解并发的基...
摘要:内存模型是围绕着在并发过程中如何处理原子性可见性和有序性这个特征来建立的,我们来看下哪些操作实现了这个特性。可见性可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。 Java内存模型是围绕着在并发过程中如何处理原子性、可见性和有序性这3个特征来建立的,我们来看下哪些操作实现了这3个特性。 原子性(atomicity): 由Java内存模型来直接保证原子性变量操作包括...
摘要:如何在线程池中提交线程内存模型相关问题什么是的内存模型,中各个线程是怎么彼此看到对方的变量的请谈谈有什么特点,为什么它能保证变量对所有线程的可见性既然能够保证线程间的变量可见性,是不是就意味着基于变量的运算就是并发安全的请对比下对比的异同。 并发编程高级面试面试题 showImg(https://upload-images.jianshu.io/upload_images/133416...
摘要:有可能一个线程中的动作相对于另一个线程出现乱序。当实际输出取决于线程交错的结果时,这种情况被称为竞争条件。这里的问题在于代码块不是原子性的,而且实例的变化对别的线程不可见。这种不能同时在多个线程上执行的部分被称为关键部分。 为什么要额外写一篇文章来研究volatile呢?是因为这可能是并发中最令人困惑以及最被误解的结构。我看过不少解释volatile的博客,但是大多数要么不完整,要么难...
阅读 1685·2021-11-18 10:02
阅读 2184·2021-11-15 11:38
阅读 2651·2019-08-30 15:52
阅读 2159·2019-08-29 14:04
阅读 3208·2019-08-29 12:29
阅读 2065·2019-08-26 11:44
阅读 958·2019-08-26 10:28
阅读 802·2019-08-23 18:37