资讯专栏INFORMATION COLUMN

浅谈Java并发编程系列(三)—— volatile型变量

zxhaaa / 824人阅读

摘要:禁止指令重排序优化。只有当线程对变量执行的前一个动作是时,才能对执行动作并且,只有当对变量执行的后一个动作是时,线程才能对变量执行动作。变量不需要与其他的状态变量共同参与不变约束。在某些情况下,的同步机制性要优于锁。

当一个变量定义为volatile之后,它具备两种特性:

保证此变量对所有线程的可见性,这里的“可见性”是指当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。

禁止指令重排序优化。

在X86处理器下通过工具获取 JIT编译器生成的汇编指令来看下volatile变量进行读写操作时CPU的行为:
Java 代码如下:

// volatile Object instance;
instance = new Singleton();

生成的汇编代码如下:

0x01a3de1d: movb $0X0, 0X1104800(%esi); 0X01a3de24: lock addl $0X0, (%esp);

有volatile变量修饰的共享变量进行写操作的时候会多出第二行汇编代码,Lock前缀的指令在多核处理器下会引发了两件事件。

将当前处理器缓存的数据写回到系统内存。

这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。

再让我们从Java内存模型的角度分析下volatile变量。假定T表示一个线程,V和W分别表示两个volatile变量,那么在进行read, load, use, assign, store和write时需要满足以下三条规则:

只有当线程T对变量V执行的前一个动作是load时,T才能对V执行use; 并且,只有当T对V执行的后一个动作是use时,T才能对V执行load。T对V的use动作可以认为是和线程T对V的load,read动作相关联,必须连续一起出现(这条规则要求 在工作内存中,每次使用V前都必须先从主内存刷新最新的值,用于保证能看见其他线程对变量V所做的修改后的值)。

只有当线程T对变量V执行的前一个动作是assign时,T才能对V执行store动作;并且,只有当T对变量V执行的后一个动作是store时,线程T才能对变量V执行assign动作。线程T对变量V的assign动作可认为是和线程T对变量V的store, write动作相关联,必须连续一起出现(这条规则要求 在工作内存中,每次修改V后都必须立刻同步回主内存中,用于保证其他线程可以看到自己对变量V所做的修改)。

假定动作A是线程T对变量V实施的use或assign操作,假定动作F是和动作A相关联的load或store动作,假定动作P是和动作F相应的变量V的read或write动作;类似的,假定动作B是线程T对变量W实施的use或assign动作,假定动作G是和动作B相关联的load或store动作,假定动作Q是和动作G相应的变量W的read或write动作。如果A先于B,那P先于Q(这条规则要求 volatile修饰的变量不会被指令重排序优化,保证代码的执行顺序与程序的顺序相同)。

由于volatile变量只能保证可见性,在不符合以下两条规则的运算场景中,仍然要通过加锁(使用synchronized或java.util.concurrent中的原子类)来保证原子性:

运行结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值。

变量不需要与其他的状态变量共同参与不变约束。

在某些情况下,volatile的同步机制性要优于锁。并且,volatile变量读操作的性能消耗与普通变量几乎没有什么差别,但是写操作则可能会慢一些,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

参考:
深入分析Volatile的实现原理

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/66049.html

相关文章

  • 浅谈Java并发编程系列(四)—— 原子性、可见性与有序性

    摘要:内存模型是围绕着在并发过程中如何处理原子性可见性和有序性这个特征来建立的,我们来看下哪些操作实现了这个特性。可见性可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。 Java内存模型是围绕着在并发过程中如何处理原子性、可见性和有序性这3个特征来建立的,我们来看下哪些操作实现了这3个特性。 原子性(atomicity): 由Java内存模型来直接保证原子性变量操作包括...

    tianren124 评论0 收藏0
  • 浅谈java中的并发控制

    摘要:并发需要解决的问题功能性问题线程同步面临两个问题,想象下有两个线程在协作工作完成某项任务。锁可用于规定一个临界区,同一时间临界区内仅能由一个线程访问。并发的数据结构线程安全的容器,如等。 并发指在宏观上的同一时间内同时执行多个任务。为了满足这一需求,现代的操作系统都抽象出 线程 的概念,供上层应用使用。 这篇博文不打算详细展开分析,而是对java并发中的概念和工具做一个梳理。沿着并发模...

    Gilbertat 评论0 收藏0
  • 我的阿里之路+Java面经考点

    摘要:我的是忙碌的一年,从年初备战实习春招,年三十都在死磕源码,三月份经历了阿里五次面试,四月顺利收到实习。因为我心理很清楚,我的目标是阿里。所以在收到阿里之后的那晚,我重新规划了接下来的学习计划,将我的短期目标更新成拿下阿里转正。 我的2017是忙碌的一年,从年初备战实习春招,年三十都在死磕JDK源码,三月份经历了阿里五次面试,四月顺利收到实习offer。然后五月怀着忐忑的心情开始了蚂蚁金...

    姘搁『 评论0 收藏0
  • Java多线程学习(volatile关键字

    摘要:三关键字能保证原子性吗并发编程艺术这本书上说保证但是在自增操作非原子操作上不保证,多线程编程核心艺术这本书说不保证。多线程访问关键字不会发生阻塞,而关键字可能会发生阻塞关键字能保证数据的可见性,但不能保证数据的原子性。 系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchroniz...

    tain335 评论0 收藏0
  • Java 并发编程(学习)

    摘要:并发编程的挑战并发编程的目的是为了让程序运行的更快,但是,并不是启动更多的线程就能让程序最大限度的并发执行。的实现原理与应用在多线程并发编程中一直是元老级角色,很多人都会称呼它为重量级锁。 并发编程的挑战 并发编程的目的是为了让程序运行的更快,但是,并不是启动更多的线程就能让程序最大限度的并发执行。如果希望通过多线程执行任务让程序运行的更快,会面临非常多的挑战:(1)上下文切换(2)死...

    NervosNetwork 评论0 收藏0

发表评论

0条评论

zxhaaa

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<