使用关键字volatile可以禁止代码的重排序;

  在Java程序运行时,JIT(即使编译器)可以动态地改变程序代码运行地顺序;例如,有如下代码:

A代码-重耗时
B代码-轻耗时
C代码-重耗时
D代码-轻耗时

  

  在多线程环境下,JIT有可能进行代码重排序,重排序后地代码顺序有可能如下:

B代码-轻耗时
D代码-轻耗时
A代码-重耗时
C代码-重耗时

  这样做地主要原因是CPU流水线是同时执行这4个指令的,那么轻耗时的代码在很大程度上先执行完成,以让出CPU流水线给其他指令,所以代码重排序是为了追求更高的程序运行的效率;

  重排序发生在没有依赖关系时,例如,对于上面的A,B,C,D代码,B,C,D代码不依赖A代码的结果,C,D代码不依赖A,B代码的结果,D代码不依赖A,B,C代码的结果,这种情况下就会发生重排序,如果代码之间有依赖关系,则代码不会重排序;

  使用关键字volatile可以禁止代码重排序,例如,有如下代码:

A变量的操作
B变量的操作
volatile Z变量的操作
C变量的操作
D变量的操作

  

  那么会有4种情况发生:

  1. A,B可以重排序
  2. C,D可以重排
  3. A,B不可以重排到Z的后面
  4. C,D不可以重排到Z的前面

  

  换言之,变量Z是一个屏障,Z变量之前或之后不可以跨越Z变量,这就是屏障的作用,关键字synchronized具有同样的特性;

  1.关键字synchronized之前的代码不可以重排到synchronized之后

   2.关键字synchronized之后的代码不可以重排到synchronized之前

  使用双重检查锁实现多线程环境下的延迟加载单例模式

public class Singleton {
private static volatile Singleton singleton; private Singleton() { } public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
} return singleton;
}
}

  

  使用volatile修饰变量singleton使该变量在多个线程间达到可见性,另外也禁止了singleton =  new Singleton()的代码重排序,singleton = new Singleton()代码在内部分为3部分:

1.memory = allocate(); //分配对象的内存空间
2.ctorInstance(memory); //初始化对象
3.instance = memory; //设置instance指向刚分配的内存地址

  

  在一些JIT编译器上,这种指令重排是真实发生的;

1.memory = allocate(); //分配对象的内存空间
3.instance = memory; //设置instance指向刚分配的内存地址
2.ctorInstance(memory); //初始化对象

  所有线程在执行Java程序时都必须要遵守intra-thread semantics;intra-thread semantics保证重排序不会改变单线程内的程序结果;换句话说,intra-thread semantics允许那些在单线程内,不会改变单线程程序执行结果的重排序;

      

  当线程A,线程B执行时,B线程访问instance所引用的对象,但这个对象没有被线程A初始化,线程B将看到一个还没有被初始化的对象;

  这里的A2和A3虽然重排序了,但Java内存模型的intra-thread semantics将确保A2一定会排在A4前面执行;因此,线程A的intra-thread semantics没有改变,但A2和A3的重排序,将会导致线程B判断instance实例不为空,线程B接下来将访问instance引用的对象(上图中线程B中的虚线),此时线程B访问到的是一个没有没有初始化的对象(没有进行赋值的对象),返回的是一个空的对象;

volatile与重排序的更多相关文章

  1. Jvm 中的 重排序、主存、原子操作

    一.重排序 好处:重排序可以提升性能,避免在一个耗时很长的指令在“执行”阶段呆很长时间,而导致后续的指令都卡在“执行”之前的阶段上. 坏处:重排序对多线程的影响 class ReorderExampl ...

  2. Java的多线程机制系列:不得不提的volatile及指令重排序(happen-before)

    一.不得不提的volatile volatile是个很老的关键字,几乎伴随着JDK的诞生而诞生,我们都知道这个关键字,但又不太清楚什么时候会使用它:我们在JDK及开源框架中随处可见这个关键字,但并发专 ...

  3. Java的多线程机制系列:(四)不得不提的volatile及指令重排序(happen-before)

    一.不得不提的volatile volatile是个很老的关键字,几乎伴随着JDK的诞生而诞生,我们都知道这个关键字,但又不太清楚什么时候会使用它:我们在JDK及开源框架中随处可见这个关键字,但并发专 ...

  4. 关于volatile的可见性和禁止指令重排序的疑惑

    在学习volatile语义的可见性和禁止指令重排序的相关测试中,发现并不能体现出禁止指令重排序的特性 实验代码如下 package com.aaron.beginner.multithread.vol ...

  5. 不得不提的volatile及指令重排序(happen-before)

    微信公众号[程序员江湖] 作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于 2018 年秋招拿到 BAT 头条.网易.滴滴等 8 个大厂 offer,目前致力于分享这几年的学习经验. ...

  6. 多线程学习:Volatile与Synchronized的区别、什么是重排序

    java线程的内存模型 java的线程内存模型中定义了每个线程都有一份自己的共享变量副本(本地内存),里面存放自己私有的数据,其他线程不能直接访问,而一些共享变量则存在主内存中,供所有线程访问. 上图 ...

  7. java并发编程的艺术(二)---重排序与volatile、final关键字

    本文来源于翁舒航的博客,点击即可跳转原文观看!!!(被转载或者拷贝走的内容可能缺失图片.视频等原文的内容) 若网站将链接屏蔽,可直接拷贝原文链接到地址栏跳转观看,原文链接:https://www.cn ...

  8. 原子性、内存可见性和重排序——重新认识synchronized和volatile

    一.原子性 原子性操作指相应的操作是单一不可分割的操作.例如,对int变量count执行count++d操作就不是原子性操作.因为count++实际上可以分解为3个操作:(1)读取变量count的当前 ...

  9. 单例模式+volatile禁止指令重排序

    单例模式: 单例,顾名思义就是只能有一个.不能再出现第二个.就如同地球上没有两片一模一样的树叶一样. 在这里就是说:一个类只能有一个实例,并且整个项目系统都能访问该实例. 单例模式共分为两大类: 懒汉 ...

随机推荐

  1. Github 个人首页的 README,这样玩儿~

    本文首发于 Ficow Shen's Blog,原文地址: Github 个人首页的 README,这样玩儿~. 内容概览 前言 创建仓库 修改 README 的内容 总结 前言 大家最近有没有发现这 ...

  2. O、Θ、Ω&主定理

    1.这些是时间复杂度的.(e.g. O(n).Θ(n).Ω(n)) 主要为主定理(坏东西) 2.本质 O <= Θ = Ω >= 3.(你可以把他们都试一遍)主要用处(目前,2020-09 ...

  3. 源码分析springboot自定义jackson序列化,默认null值个性化处理返回值

    最近项目要实现一种需求,对于后端返回给前端的json格式的一种规范,不允许缺少字段和字段值都为null,所以琢磨了一下如何进行将springboot的Jackson序列化自定义一下,先看看如何实现,再 ...

  4. Combine 框架,从0到1 —— 5.Combine 中的 Subjects

    本文首发于 Ficow Shen's Blog,原文地址: Combine 框架,从0到1 -- 5.Combine 中的 Subjects. 内容概览 前言 PassthroughSubject C ...

  5. Redis 4.0.2安装与卸载

    安装 使用root用户安装: 1.wget http://download.redis.io/releases/redis-4.0.2.tar.gz 2.tar -zxvf redis-4.0.2.t ...

  6. Centos-zip压缩-文件或目录-zip unzip

    zip uzip 将一般文件或者目录进行压缩或者解压,默认以 .zip为后缀名 zip 相关选项 -r 递归压缩目录 -d 从压缩包中删除指定文件 -i  压缩指定文件列表文件 -x 压缩排除指定文件 ...

  7. Effective C++ 读书笔记 名博客

    https://www.cnblogs.com/harlanc/tag/effective%20c%2B%2B/default.html?page=3

  8. 052 01 Android 零基础入门 01 Java基础语法 05 Java流程控制之循环结构 14 Eclipse下程序调试——debug2 多断点调试程序

    052 01 Android 零基础入门 01 Java基础语法 05 Java流程控制之循环结构 14 Eclipse下程序调试--debug2 多断点调试程序 本文知识点: Eclipse下程序调 ...

  9. matlab中strcmpi比较字符串(不区分大小写)

    来源:https://ww2.mathworks.cn/help/matlab/ref/strcmpi.html?searchHighlight=strcmpi&s_tid=doc_srcht ...

  10. 【题解】[USACO07NOV]Sunscreen G

    \(Link\) \(\text{Solution:}\) 把奶牛的忍耐度转化为线段,则题目转化为选择一些点使得覆盖的线段尽可能多.一个点只能覆盖一条线段. 考虑将点按照位置排序,线段按照右端点排序. ...