Java并发编程之验证volatile指令重排-理论篇

Java并发包下的类中大量使用了volatile关键字。通过之前文章介绍,大家已经知道了volatile的三大特性:共享变量可见性;不保证原子性;禁止指令重排后顺序性。通过前面两篇文章我们通过代码验证了前两个特性,本文我们就来验证禁止指令重排保证顺序性。

指令重排序的生活例子

去餐厅吃饭预定位置的的时候。假设要去A餐厅吃饭,A餐厅有前台B、服务员C以及老板D。如果就只有你一个人去吃饭的时候,你给前台或者给服务器或者给老板说一声把2号桌预定了,半小时后过来。餐厅在为了2小时内就你一个人去吃饭。那么OK,没问题,别说等半个小时,就是等一个小时,2号桌还是你的。

但是,如果现在是吃饭高峰期,很多人来吃饭,你给前台说了,前台忙着没有及时给服务员或者没有给老板说,这个时候有个路人甲来吃饭,刚好看到2号桌没人,老板或者服务员就让他就坐2号桌吃饭了。那么,等你过来的时候,2号桌已经有人了。这个时候对于你来说,这个结果就不是你想要的了。

上面案例,如果从计算机执行指令角度来分析的话,你要到2号桌吃饭,这是预期结果。餐厅A就相当于是处理器,前台B就相当于是编译器,服务员C和老板D就是指令和内存系统。如果你预定的时间点不是吃饭高峰期或者没有人去餐厅A吃饭。那么你就相当于是一个线程。就是单线程的。老板、前台、服务员怎么安排都可以。因为只有你一个2号桌肯定是你的。这是单线程情况下。预期结果与实际结果就是一致的。

如果你预定的时间点是吃饭高峰期,很多人来吃饭(很多线程),这个时候为了餐厅效益,无论是前台还是服务员或者是老板都会对你的位置进行重排序。在你没有来的时候,会安排其他人到你预定的位置吃饭。如果其他人在你的位置吃饭,这个时候你再来吃饭,那么实际结果和预期结果就不一样了。这个时候餐厅应该做出相应的赔偿。为了解决这种赔偿问题,老板就想到了一个方案。做个牌子放在客人预定的桌子上。

当前台或者是服务员或者是老板看到餐桌上放的这个牌子,就知道这个位置不能再调动了。其中这个放在餐桌上的牌子就是特殊类型的内存屏障了。

示意图如下:

再来举个更常见的例子:

考试,在考试的时候老师会告诉我们,先做会做的,不会做的放到后面做。假设出题老师出题顺序是1-5,但是考试会根据自己实际情况做题顺序有可能是1、2、4、5、3或者是1、3、4、5、2等等。如果把出题老师看着是写代码的程序员,题目的顺序是代码一行一行的顺序,你的老师会告诉你先做会做的,此时老师就相当于是编译器,会排序一次。然后你自己做的时候又会进行重新排序,你自己就相当于是处理器又排序了一次。

上面两个现实生活中的案例,我们弄明白后,再来看看在计算机中指令重排问题,就很容易理解了。

指令重排

我们程序员编写的代码在JVM执行的时候,为了提高性能,编译器和处理器都会对代码编译后的指令进行重排序。分为3种:

1:编译器优化重排:

编译器的优化前提是在保证不改变单线程语义的情况下,对重新安排语句的执行顺序。

2:指令并行重排:

如果代码中某些语句之间不存在数据依赖,处理器可以改变语句对应机器指令的顺序

如:int x = 10;int y = 5;对于这种x y之间没有数据依赖关系的,机器指令就会进行重新排序。但是对于:int x = 10; int y = 5; int z = x+y;这种的,因为z和x y之间存在数据依赖(z=x+y)关系。在这种情况下,机器指令就不会把z排序在xy前面。

3:内存系统的重排序

通过之前的学习,我们知道了处理器和主内存之间还存在一二三级缓存。这些读写缓存的存在,使得程序的加载和存取操作,可能是乱序无章的。

指令重排序的流程图

通过上面介绍,我们可以知道从程序员写的Java源码到处理器真正实际执行的指令序列,会经历如下图的过程:

执行顺序:

源码编译器优化重排序(第一次排序) 指令重排序(第二次)内存重排序(第三次) 最终指向的指令。

无论是第一次编译器的重排序还是第二、三次的处理器重排序。这些重排序当在多线程的场景下可能会出现线程可见性的问题。

如在多线程的情况下,单例模式就不安全了。

为了解决这个问题,JMM允许编译器在生成指令顺序的时候,可以插入特定类型的内存屏障来禁止指令重排序。

当一个变量使用volatile修饰的时候,volatile关键字就是内存屏障。当编译器在生成指令顺序的时候,发现了volatile,就直接忽略掉。不再重排序了。

示意图:

证明volatile禁止指令重排演示代码,欢迎继续学习下一篇文章

欢迎关注凯哥公众号:凯哥Java(kaigejava)

Java并发编程之验证volatile指令重排-理论篇的更多相关文章

  1. Java并发编程之验证volatile不能保证原子性

    Java并发编程之验证volatile不能保证原子性 通过系列文章的学习,凯哥已经介绍了volatile的三大特性.1:保证可见性 2:不保证原子性 3:保证顺序.那么怎么来验证可见性呢?本文凯哥(凯 ...

  2. Java并发编程之三:volatile关键字解析 转载

    目录: <Java并发编程之三:volatile关键字解析 转载> <Synchronized之一:基本使用>   volatile这个关键字可能很多朋友都听说过,或许也都用过 ...

  3. Java并发编程基础之volatile

    首先简单介绍一下volatile的应用,volatile作为Java多线程中轻量级的同步措施,保证了多线程环境中“共享变量”的可见性.这里的可见性简单而言可以理解为当一个线程修改了一个共享变量的时候, ...

  4. Java并发编程学习:volatile关键字解析

    转载:https://www.cnblogs.com/dolphin0520/p/3920373.html 写的非常棒,好东西要分享一下 Java并发编程:volatile关键字解析 volatile ...

  5. 干货:Java并发编程系列之volatile(二)

    接上一篇<Java并发编程系列之synchronized(一)>,这是第二篇,说的是关于并发编程的volatile元素. Java语言规范第三版中对volatile的定义如下:Java编程 ...

  6. Java并发编程知识点总结Volatile、Synchronized、Lock实现原理

    Volatile关键字及其实现原理 在多线程并发编程中,Volatile可以理解为轻量级的Synchronized,用volatile关键字声明的变量,叫做共享变量,其保证了变量的“可见性”以及“有序 ...

  7. Java并发编程里的volatile。Java内存模型核CPU内存架构的对应关系

    CPU内存架构:https://www.jianshu.com/p/3d1eb589b48e Java内存模型:https://www.jianshu.com/p/27a9003c33f4 多线程下的 ...

  8. 【Java并发编程】:volatile变量修饰符

    volatile用处说明     在JDK1.2之前,java的内存模型实现总是从主存(即共享内存)读取变量,是不需要进行特别的注意的.而随着JVM的成熟和优化,现在在多线程环境下volatile关键 ...

  9. Java并发编程(二):volatile关键字

    volatile是Java虚拟机提供的轻量级的同步机制.volatile关键字有如下两个作用,一句话概括就是内存可见性和禁止重排序. 1)保证被volatile修饰的共享变量对所有线程总是可见的,也就 ...

  10. 【转】关于Java并发编程的总结和思考

    一.前言 就是想学习Java并发编程了,所以转载一下这篇认为还不错的博客~ 二.正文 编写优质的并发代码是一件难度极高的事情.Java语言从第一版本开始内置了对多线程的支持,这一点在当年是非常了不起的 ...

随机推荐

  1. 一款EF Core下高性能、轻量级针对分表分库读写分离的解决方案

    前言 今天大姚给大家分享一款EF Core下高性能.轻量级针对分表分库读写分离的解决方案,开源(Apache License)的EF Core拓展程序包:ShardingCore. ShardingC ...

  2. JpaRepository:Paging query needs to have a Pageable parameter! Offending method public abstract

    在练习 Spring Data JPA 时,使用分页接口 Pageable 查询数据,接口实现后,运行报错: Paging query needs to have a Pageable paramet ...

  3. Java常见问题-汇总

    一.面试到底在问些什么东西? 首先你要知道,面试官的提问和你简历上写的内容是紧密联系的,所以你简历上写的技能一定要会. 一般面试包括下面几方面知识类型: Java基础.多线程.IO与NIO.虚拟机.设 ...

  4. 创建数据库时排序规则utf8_general_ci与utf8_bin的区别

    在MySQL数据库中,字符集(如utf8)定义了字符如何存储,而排序规则(Collation)则定义了字符如何比较.排序和区分大小写.utf8_general_ci和utf8_bin是两种常用的UTF ...

  5. oeasy教您玩转vim - 13 - # 大词小词

    大词小词 回忆上节课内容 我们上次学习了 e e 代表 end 词尾 自有跳跃 还可以成倍次数的跳跃 但其实我是想以一个一个属性地跳跃,有没有方法呢? 查询帮助 没思路的话我们还是得继续查询 :h w ...

  6. oeasy教您玩转vim - 8 - # 追加文本

    追加文本 回忆上节课内容 我们这次深入了 i 命令 i 在当前的光标之前插入 I 在本行文本的最前面插入 还有一些常用的编辑命令 . 重做 u 撤销 ctrl+r 还原 关于插入,还有什么讲究吗? 类 ...

  7. 【教程】重启Windows文件资源管理器

    [教程]重启Windows文件资源管理器 打开任务管理器 以下方法任选其一: 方法一 :组合键 Ctrl + Shift + ESC (个人推荐) 方法二 :组合键 Win + X (或右键Windo ...

  8. mybatisplus实现一次多表联查+分页查询

    众所周知,mybatisplus非常好用,但是他不好用就不好用在不可以多表联查.在mybatisplusjoin中提供了联查的方法,那个参数我没看懂Orz 不过,历经千辛万苦,我通过xml终于写出来了 ...

  9. tcp udp测试

    tcp udp测试 sub_udp.py #!/usr/bin/env python # -*- coding: utf-8 -*- # 可以正常接收udp import socket import ...

  10. 我用Awesome-Graphs看论文:解读GraphBolt

    GraphBolt论文:<GraphBolt: Dependency-Driven Synchronous Processing of Streaming Graphs> 前面通过文章&l ...