happen-before与volatile、final

2024-02-16 04:18
文章标签 final volatile happen

本文主要是介绍happen-before与volatile、final,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

重排序与内存可见性问题

  重排序有三种:(1)编译器重排序:编译器可以对没有先后依赖关系的语句重新排序。(2)CPU指令重排序:对没有依赖关系的指令重新排序。(3)CPU内存重排序:指令执行顺序和CPU缓存写入主内存的顺序不一致。
  内存可见性问题主要是第三种重排序造成的。以下面例子为例:
线程1:X = 1 ; a = Y
线程2:Y = 1 ; b = X
理论上来看,最后可能出现三种情况:1、a = 0,b = 1。2、a = 1,b = 1。3、a = 1,b = 0。
但是,a和b都为0也是可能出现的,有可能线程1将X = 1写到了自己的CPU缓存中但还没写入到主内存中,那么线程2就无法将b设置为1,就可能出现a和b都为0。

happened-before的概念

  如果A happened-before B,那么A的执行结果B必须可见。在JMM的happened-before描述的规范中,volatile变量不能重排序,对synchronized的解锁happpened-before后续对该锁的加锁。

happened-before的传递性

  若A happened-before B,B happened-before C,那么A happened-before C。

final关键字的happened-before语义

  对于final域的写happened-before与后续对该域的读。对于final域的读happened-before后续对该域的读。

volatile的happend-before

  在CPU层面可以将JDK的内存屏障分为四种:
(1)LoadLoad: 禁止读和读的重排序。
(2)StoreStore:禁止写和写的重排序。
(3)LoadStore:禁止读和写的重排序。
(4)StroeLoad:禁止写和读的重排序。
  JDK的Unsafe类提供三个内存屏障函数:

	public native void loadFence();public native void storeFence();public native void fullFence();

  其中,loadFence = LoadLoad + LoadStore,storeFence = StoreStore + LoadStore,fullFence = loadFence + storeFence + StoreLoad。
  volatile实现原理:
(1)在volatile写操作前插入一个StoreStore屏障,使volatile写操作不会和之前的写操作重排序。
(2)在volatile写操作后插入一个StoreLoad屏障,使volatile写操作不会和后面的读操作重排序。
(3)在volatile读操作后面插入一个LoadLoad屏障和LoadStore屏障,使volatile读操作不会和后面的读操作、写操作重排序。

总结:happen-before四个基本规则

(1)单线程中的每个操作都happen-before后面的操作。
(2)对volatile的写,happen-before后面对该变量的读。
(3)对synchronized的解锁happen-before后续对该锁的加锁。
(4)对final变量的写,happen-before对final域对象的读,happen-before后续对final变量的读。


参考资料:《Java并发实现原理:JDK源码剖析》

这篇关于happen-before与volatile、final的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/713474

相关文章

关键字synchronized、volatile的比较

关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰于变量,而synchronized可以修饰方法,以及代码块。随着JDK新版本的发布,synchronized关键字的执行效率上得到很大提升,在开发中使用synchronized关键字的比率还是比较大的。多线程访问volatile不会发生阻塞,而synchronize

关键字volatile有什么含意?

1. 并行设备的硬件寄存器。存储器映射的硬件寄存器通常加volatile,因为寄存器随时可以被外设硬件修改。当声明指向设备寄存器的指针时一定要用volatile,它会告诉编译器不要对存储在这个地 址的数据进行假设,也就是要去相应的内存地址里取。 2. 一个中断服务程序中修改的供其他程序检测的变量。volatile提醒编译器,它后面所定义的变量随时都有可能改变。因此编译后的程序每次需要存储或读取这

java内存模型及volatile关键字

文章目录 一、基本概念二、java内存模型三、volatile关键字 一、基本概念 在并发编程过程中,我们经常会遇到三类问题:原子性问题,可见性问题,有序性问题。下面我们来介绍一下和这些问题相关的三个概念。 1.原子性 也就是执行一个操作,要不全部执行成功,要不执行失败。比如a=0,这个操作就是原子性的,要么赋值成功,要么赋值失败。再比如a++操作,这个操作就不是原子性的,它是三

【HDU】3986 Harry Potter and the Final Battle 最短路

传送门:【HDU】3986 Harry Potter and the Final Battle 题目分析:先求一次最短路,同时记录在最短路上的顶点以及以该顶点为弧尾的最短路上的边。然后枚举删除每一条边,分别求一次最短路,其中最大的即答案。当然不可达输出-1。 测试发现堆优化的dij不如slf优化的spfa。。可能图太稀疏了吧。。。反正我觉得我写的挺搓的了。。。 代码如下:

JBoss AS 7.1.1.Final 安装

JBoss AS 7.1.1.Final下载地址:http://download.jboss.org/jbossas/7.1/jboss-as-7.1.1.Final/jboss-as-7.1.1.Final.zip localhost:9990/console 用户root 密码 1234 安装参考:http://my.oschina.net/thinker4self/blog/273527

Java并发编程--深入理解volatile关键字

前言 一个月以前就准备写篇关于volatile关键字的博客,一直没有动笔,期间看了大量的文章,发现一个小小volatile关键字竟然涉及JMM(Java memory model),JVM(Java virtual machine),Java多线程同步与安全各个方面的知识,写起了非常的困难,后面附带的参考文献仅仅是我看过文献的一部分。 Java memory model(Java内存模型)

final_封装_多态_servletJAVA043-047

来源:http://www.bjsxt.com/ 1、S01E043_01final修饰变量、方法和类 (1)修饰变量:常量; (2)修饰方法:该方法不可被子类重写,但可以被重载; (3)修饰类:修饰的类不能有子类,不能被继承。如:Math、String。 2、S01E044_01面向对象三大特性之一:封装/隐藏(encapsulation) (1)封装的作用:隐藏对象内部的复杂性,只

《从C/C++到Java入门指南》- 25.final 关键字

final 关键字 final 变量 final变量可以理解为C++中的const,变量一经定义无法修改。 public class Main {public static void main(String args[]) {final double PI = 3.1415926;System.out.println(PI);// PI = 3.14; // 尝试修改会报错}} fina

java volatile变量及其使用场景

java中的一种稍弱的同步机制,就是volatile变量,用于确保将变量的更新操作通知到其他线程。 变量声明为volatile后: (1)编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序(重排序不懂的,可以自行百度,需要理解)。 (2)volatile变量不会被缓存在寄存器或对其他处理器不可见的地方 因此volatile变量总是会返回最新的值。

插件开发遇到的坑------final 型变量,编译过程被优化

android 插件开发遇到的坑 今天遇到一个坑,pdf 插件,调用了主工程的一个静态final 字符串,但是主工程里面已经没有这个字符串了,却没有崩溃。 后来同事说,因为字符串可能已经直接被写死了。你应该看下编译后的代码。我一同事,反编译apk 之后,发现,果然,因为provide 的jar 里面是有这个final 的字符串的,java 编译的时候,直接用死的值替换掉了。这样,