并发编程JMM系列之重排序和顺序一致性

2023-11-06

前言

昨天我们接触到了什么是Java内存模型以及两种Java并发模型,并对JMM有了一些初步的认识和了解,我们在上节有提到JMM的重排序规则,但是讲的不详细,今天我们再重点聊下重排序这个东西,以及顺序一致性内存模型,OK,开始我们今天的并发之旅吧。

重排序要遵循的三个原则

重排序的目的是提高编译器和处理器的并行处理能力即并行度,但是在做重排序时,一般都会遵循下面3种原则:

数据依赖性原则:如果两个操作访问同一个变量,并且这两个操作中有一个操作是写操作,那么这两个操作之间就存在数据依赖性,上节我们知道编译器和处理器都会进行重排序,其实在进行重排序时都会遵循数据依赖性原则,编译器和处理器不会去改变存在数据依赖关系的两个操作的执行顺序;这就是数据依赖性原则;

当然,数据依赖性指的是在单线程的情况下,多线程情况下数据依赖性不被编译器和处理器考虑;

as-if-serial原则:无论怎么做重排序,程序的执行结果不能被改变,这就是as-if-serial原则;

程序顺序规则:由先行发生原则我们知道,如果A-B,B-C,那么A-C一定成立,但是存在下面一种情况:

                            

根据先行发生A-B-C顺序执行,但是此时我们会发现其实B操作并不依赖A操作的结果,而C操作需要A和B操作的结果可见,所以C的顺序不能重排序,而B可以先执行于A,这种情况下,不仅重排序了,而且B-A-C的执行结果跟A-B-C执行结果一致,这种情况下的重排序JMM认为是合法的,JMM允许这种重排序,这种规则的本意还是在不改变执行结果的前提下,尽可能的提高并行度。

 

重排序对多线程的影响

看下面这段demo:

int a;
 int b;
 public void set() {
   a = 1;
   b = 2;
 }

 public void get() {
   if (b == 2) {
     a++;
   }
   System.out.println(a);
 }

假设set方法由线程1执行,get方法由线程2执行,首先正常没有做重排序下的执行结果:

假设线程1中的set方法,进行了重排序:

再假设线程2种的get方法进行了重排序:

或者:线程2先于线程一执行时,结果又不一样,如下所示

综上我们会发现,重排序在破坏了多线程的语义,所以说在多线程下对存在控制依赖的操作重排序,可能会改变程序的执行结果。

顺序一致性内存模型

顺序一致性内存模型,是一个理论参考模型,我们主要从下面几点展开对顺序一致性内存模型的讲解。

 

数据竞争与顺序一致性

当程序未进行正确同步时就会存在数据竞争问题,那么什么是数据竞争呢?JMM对数据竞争做了如下定义:

在一个线程中写一个变量,在另一个线程中读这个变量,而且写和读没有通过同步来进行排序。

如果一个多线程程序能正确的同步,那么这个程序将不存在数据竞争,JMM对正确同步的多线程程序的内存一致性做了如下保证:

如果程序是正确同步(指广义上的同步,包括常见同步原语volatile、synchronized等)的,那么程序的执行将具有顺序一致性,也就是说,程序的执行结果与程序在顺序一致性内存模型中的执行结果一致。

顺序一致性内存模型

顺序一致性内存模型有两大特性:

  • 一个线程中的所有操作必须按照程序的顺序来执行

  • 所有线程都只能看到一个单一的操作执行顺序

顺序一致性模型如下图:

为了更好的理解,对顺序一致性模型我们再看下面这个例子,假设有两个线程A和B,每个线程都执行3个操作,我们分别看下在线程同步和未同步下,顺序一致性和JMM下的执行结果:

 

同步程序如何保证执行结果一致性

在顺序一致性模型中:所有的操作完全按程序的顺序串行执行,所以同步后的程序是能保证程序执行结果一致性的;

在JMM中:临界区的代码可以重排序,JMM在退出临界区和进入临界区这两个关键时间点做了一些特别处理,使得线程在这两个时间点具有与顺序一致性模型相同的内存视图;

我们还以上面那个例子分析下这两种情况下,同步是如何保证结果一致性的,分析见下图:

我们可以发现,JMM中线程1和线程2在临界区都发生了重排序,而且这种重排序并不会影响结果,在某些情况下,这种重排序是可以提高执行效率的。

 

以上就是我们对JMM重排序和顺序一致性模型的讲解,同时也将顺序一致性模型和JMM模型做了一些对比,希望几分钟的阅读能给你带来收益!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

并发编程JMM系列之重排序和顺序一致性 的相关文章

  • 【Java并发编程的艺术】Java并发容器和框架:Java中的阻塞队列

    1 什么是阻塞队列 阻塞队列常用于生产者和消费者的场景 生产者是向队列里添加元素的线程 消费者是 从队列里取元素的线程 阻塞队列就是生产者用来存放元素 消费者用来获取元素的容器 阻塞队列 BlockingQueue 是一个支持两个附加操作的
  • 方法锁,对象锁,类锁的区别和用法

    在java编程中 经常需要用到同步 而用得最多的也许是synchronized关键字了 下面看看这个关键字的用法 因为synchronized关键字涉及到锁的概念 所以先来了解一些相关的锁知识 java的内置锁 每个java对象都可以用做一
  • Cuda Streams的概述(一)-- Cuda介绍

    最近在做有关Cuda的一个项目 碰到匪夷所思的问题 在异步的时候发现并没有达到预期的效果 程序没有异步起来 然后在网上找了一个Nvida的有关Cuda Streams的一个ppt 然后照着里面的提示 使程序达到了异步的效果 首先 先回顾一下
  • java.util.concurrent.locks.ReentrantReadWriteLock 读写锁

    读写锁简介 对共享资源有读和写的操作 且写操作没有读操作那么频繁 在没有写操作的时候 多个线程同时读一个资源没有任何问题 所以应该允许多个线程同时读取共享资源 但是如果一个线程想去写这些共享资源 就不应该允许其他线程对该资源进行读和写的操作
  • Java多线程中常见错误梳理,新手程序员必看

    很多Java新手在刚接触线程时都会被其复杂的知识点搞晕 在实际应用中同样错误不断 如何才能快速掌握多线程呢 常见的Java多线程错误有哪些 接下来就给大家分享Java新手学习入门中多线程失误梳理 无论是客户端还是服务器端多线程Java程序
  • MPI测试程序

    include
  • 并发编程JMM系列之重排序和顺序一致性

    前言 昨天我们接触到了什么是Java内存模型以及两种Java并发模型 并对JMM有了一些初步的认识和了解 我们在上节有提到JMM的重排序规则 但是讲的不详细 今天我们再重点聊下重排序这个东西 以及顺序一致性内存模型 OK 开始我们今天的并发
  • Java并发编程系列 - Java内存模型

    Java并发编程 可见性 原子性 有序性 Java内存模型如何解决可见性 有序性 并发问题产生的根源 可见性 原子性 有序性 可见性 Java内存模型的主要目标是定义程序中变量的访问规则 即在虚拟机中将变量存储到主内存或者将变量从主内存取出
  • 并发编程系列之volatile内存语义

    前言 前面介绍顺序一致性模型时 我们提到了程序如果正确的同步就会具备顺序一致性 这里所说的同步泛指广义上的同步 其中包括就包括同步原语volatile 那么volatile声明的变量为什么就能保证同步呢 这又是如何实现的呢 今天就让我们一起
  • MPI与main()程序中的其他函数执行次数

    我原先以为只有在MPI代码区域 即MPI Init argc argv 到MPI Finalize 中的代码才会涉及到进程通信的问题 但实际上在MPI区域外的代码依然受到影响 执行的次数与开启的进程数有关 为此可以使用MPI 秩 rank
  • Java并发编程的相关知识(7)-阻塞队列

    阻塞队列 ArrayBlockingQueue LinkedBlockingQueue ProiorityBlockingQueue DelayQueue SynchronousQueue LinkedTransferQueue Linke
  • 【并发】并发

    并发 进程和线程 进程 资源分配的基本单位 可以理解为在内存中运行的程序 每个进程都有独立的内存空间 一个进程包含多个线程 线程 任务执行的基本单位 负责进程中任务的执行 每个线程共享进程的内存空间 一个线程使用时 其他线程必须等待 用户
  • JUC学习笔记及拓展

    本文为自己整理的学习笔记及学习心得 大纲取自尚硅谷的JUC视频 感兴趣的小伙伴可以去B站自学 JUC学习笔记及拓展 Java JUC 1 Java JUC简介 2 volatile 关键字 内存可见性 2 1 内存可见性 2 2 volat
  • 并发策略之分工原则

    本文主要思想来自 Java虚拟机并发编程 薛笛 译 为什么要用并发 并发是再在有限的资源下提高性能的有效手段 当然现在互联网环境下并发访问的现象也比比皆是 但是本文并不涉及处理并发访问 而是使用并发手段解决复杂任务的策略 另外关于并发和并行
  • Java并发之锁

    Java并发之锁 一 临界区 二 线程安全 三 解决临界区线程安全问题 四 Java对象头 五 重量级锁 Monitor 5 1 synchronized 5 1 1 synchronized加锁流程 六 轻量级锁 6 1 轻量级锁加锁流程
  • C/C++基于线程的并发编程(二):线程安全和线程锁

    线程安全 所谓线程安全不是指线程的安全 而是指内存的安全 线程是由进程所承载 所有线程均可访问进程的上下文 意味着所有线程均可访问在进程中的内存空间 这也是线程之间造成问题的潜在原因 当多个线程读取同一片内存空间 变量 对象等 时 不会引起
  • 线程的状态与切换

    Java中的线程的生命周期大体可分为5种状态 1 新建 初始化 NEW 新创建了一个线程对象 2 可运行 RUNNABLE 线程对象创建后 其他线程 比如main线程 调用了该对象的start 方法 该状态的线程位于可运行线程池中 等待被线
  • 多线程实现事务回滚

    多线程实现事务回滚 特别说明CountDownLatch CountDownLatch的用法 CountDownLatch num 简单说明 主线程 mainThreadLatch await 和mainThreadLatch countD
  • brpc源码解析(十七)—— bthread上的类futex同步组件butex详解

    文章目录 一 futex简介 二 butex源码解析 2 1 butex相关数据结构 2 2 butex主要机制 2 2 1 butex wait 2 2 2 butex wake 我们知道在linux 下 锁和其他一些同步机制都会用到fu
  • 并发编程4 - 线程状态、死锁及ReentrantLock

    文章目录 一 再述线程状态转换 二 多把锁与线程活跃性问题 1 多把锁 2 活跃性 三 ReEntrantLock 1 基本用法 2 可重入 3 可打断 4 锁超时 5 公平锁 6 条件变量 一 再述线程状态转换 情况1 New RUNNA

随机推荐

  • 【单片机毕业设计】【mcuclub-306】万年历电子时钟

    设计简介 项目名 基于单片机的万年历电子时钟的设计 基于单片机的多功能时钟的设计 基于单片机的数字时钟的设计 单片机 STC89C52 功能简介 1 通过DS1302实时获取时间 并掉电保存时间 2 通过DS18B20获取环境温度值 3 通
  • lodash的2个数组对象操作

    根据数组对象 下的属性名称 来返回相应 的值 数据格式如下 var data test1 test2 test3 test4 test5 var key test1 对象的每个属性名称不是相同的 对应的值 是一个数组 方法1 将data初始
  • C语言中的逻辑判断

    C语言中的逻辑判断 C语言中的逻辑判断是以真和假来表示的 0为假 一切非零为真 这里举几个例子 来让读者更加深入地了解判断语句 逻辑值 int a 5 int b 3 int c a gt b 我们来看c的结果 这时c 1 因为a gt b
  • android 9安装教程,android 9.x 实现应用内更新安装

    折腾了3天 总算解决了问题 依赖如下 implementation androidx core core 1 0 2 implementation com android support support compat 28 0 0 supp
  • 【题目】pyCharm 专业版 和 社区版的区别以及如何查看其版本

    时间 2018 09 22 题目 pyCharm 专业版 和 社区版的区别以及如何查看其版本 参考链接 https zhidao baidu com question 584331885111670725 html 一 pyCharm 专业
  • 用lld替代gld生成glibc

    用lld替代gld生成glibc 首先明确 lld是llvm中的链接器 要使用lld替代gld 则要先生成lld 然后再做个软链接使gnu找gld的时候找到的是lld 这样算是替代成功 其中会遇到很多问题 逐个击破最终成功使用lld生成gl
  • sqlserver的安装详解

    背景 最近课程需求 需要安装sqlserver 虽然网上有许多教程 但是自己去动手还是出现了许多问题 感觉网上教程不是很详细 所以将自己安装的经验写一个汇总 提供给大家共同学习 实验环境 win8 1 一 sqlserver2008R2的下
  • 从封装变化的角度看设计模式——对象创建

    封装变化之对象创建 在对象创建的过程中 经常会出现的一个问题就是通过显示地指定一个类来创建对象 从而导致紧耦合 这是因为创建对象时指定类名将使你受特定实现的约束而不是特定接口的约束 这会使未来的变化更加复杂 要避免这种情况 就应该间接地创建
  • 提取字符串单引号内的字符——Python for循环代码“异想天开”

    字符串处理 不用re模块 直接用for 手撕 字符串 提取单引号内字符串 本文获得CSDN质量评分 89 学习的细节是欢悦的历程 Python 官网 https www python org Free 大咖免费 圣经 教程 python 完
  • 树结构的自定义及基本算法(Java数据结构学习笔记)

    数据结构可以归类两大类型 线性结构与非线性结构 本文的内容关于非线性结构 树的基本定义及相关算法 关于树的一些基本概念定义可参考 维基百科 树的ADT模型 根据树的定义 每个节点的后代均构成一棵树树 称为子树 因此从数据类型来讲 树 子树
  • 提升效率,主流自动化运维工具推荐

    提升效率 主流自动化运维工具推荐 自动化运维工具可以帮助运维团队提高效率 减少错误 并实现持续交付 下面将介绍几个主流的自动化运维工具 分析其应用场景 并提供各自的官网地址供深入了解 1 Ansible 官网地址 https www ans
  • VUE启动问题(You may use special comments to disable some warnings)

    vue启动时出现以下问题 出现问题的原因 Eslint的检测机制 解决方法 在build webpack base conf js文件中注释掉第44行代码 如下图 重新yarn start 或者npm run dev 解决方法 在confi
  • 2014腾讯软件开发类笔试题(广州站)

    考试时长 120分钟 一 不定项选择题 共25题 每题4分 共100分 少选 错选 多选均不得分 1 已知一棵二叉树 如果先序遍历的节点顺序是 ADCEFGHB 中序遍历是 CDFEGHAB 则后序遍历结果为 D A CFHGEBDA B
  • 如何实现云数据治理中的数据安全?

    摘要 云计算被定义为计算资源的共享池 已经在不同的应用领域广泛部署和使用 在云计算中 数据治理在提高整体性能和确保数据安全方面发挥着至关重要的作用 本研究从管理和技术应用两方面探讨如何实现云数据治理中的数据安全 关键词 大数据 云计算 数据
  • 2021-03-25

    pytorch IndexError scatter Expected dtype int64 for index 1 问题产生原因及解决方法 scatte r 要求数据是int64类型 检查传入scatter 函数的tensor 类型是不
  • Linux服务器系统内存监控方法详解

    Linux服务器系统内存监控方法详解 内存是Linux内核所管理的最重要的资源之一 内存管理系统是操作系统中最为重要的部分 因为系统的物理内存总是少于系统所需要的内存数量 虚拟内存就是为了克服这个矛盾而采用的策略 系统的虚拟内存通过在各个进
  • js 实现php md5加密,js实现md5加密插件代码分享

    本文主要和大家分享js实现md5加密插件代码 希望能帮助到大家 使用方法 引入文件 var MD5 MD5 createMD5String yourSrting function globle factory 判断执行环境是否为浏览器 ty
  • RF 浏览器与浏览器驱动 浏览器与驱动

    历史版本的火狐 http ftp mozilla org pub firefox releases 火狐驱动 https github com mozilla geckodriver releases IE驱动 iedriverserver
  • 【深度学习】在学习pytorch时,一些不会的python语法总结(1)

    文章目录 一 builtin function or method object has no attribute size 二 获取张量的四个维度 torch里的size 函数 三 改变tensor形状的函数view 一 builtin
  • 并发编程JMM系列之重排序和顺序一致性

    前言 昨天我们接触到了什么是Java内存模型以及两种Java并发模型 并对JMM有了一些初步的认识和了解 我们在上节有提到JMM的重排序规则 但是讲的不详细 今天我们再重点聊下重排序这个东西 以及顺序一致性内存模型 OK 开始我们今天的并发