【并发基础】Happens-Before模型详解
目录
一、Happens-Before模型简介
二、组成Happens-Before模型的八种规则
2.1 程序顺序规则(as-if-serial语义)
2.2 传递性规则
2.3 volatile变量规则
2.4 监视器锁规则
2.5 start规则
2.6 Join规则
一、Happens-Before模型简介
除了显示引用volatile关键字能够保证可见性以外,在Java中,还有很多的可见性保障的规则。
从JDK1.5开始,引入了一个happens-before的概念来阐述多个线程操作共享变量的可见性问题。所以我们可以认为在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作必须要存在 happens-before关系。这两个操作可以是同一个线程,也可以是不同的线程。
JMM可以通过happens-before关系向程序员提供跨线程的内存可见性,即如果A线程的写操作a与B线程的读操作b之间存在happens-before关系,尽管a操作和b操作不在同一个线程中执行,但是JMM向程序员保证a操作对b操作可见。具体定义为:
- 如果一个操作happens-before另外一个操作(发生在另一个操作之前),那么第一个操作的执行结果对第二个操作可见,且第一个操作的执行顺序在第二个操作之前。
- 两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须要按照happens-before关系指定的顺序来执行。如果重排序之后的执行结果,与按happens-before关系来执行的结果一致,那么这种重排序并不非法(也就是说,JMM允许这种重排序)。
上面两条规则,第一条,是JMM对程序员的保证。从程序员的角度可以这样理解happens-before:如果A happens-before B ,那么java内存模型将向程序员保证————A操作的结果一定对B操作可见,且A的执行顺序一定在B的前面。第二条是JMM对编译器和处理器重排序的约束原则,只要不改变程序的执行结果,编译器和处理器怎么优化都行。
二、组成Happens-Before模型的八种规则
happens-before模型是JMM要求多个操作必须满足的关系模型,而happens-before模型是由八条具体规则组成的,具体如下:
- 程序顺序规则(as-if-serial语义):一个线程中的每个操作,happens-before于该线程的任意后续操作。
- 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
- volatile变量规则:对一个volatile域的写,happens-before于任意后续对该volatile域的读。
- 传递性规则:如果A happens-before B,且B happens-before C,那么A happens-before C。
- start()规则: 如果线程A执行ThreadB.start(),那么A线程的ThreadB.start()操作happens-before于线程B的任意操作。
- join()规则: 如果线程A执行ThreadB.join(),那么B线程中的任意操作happens-before于线程A从ThreadB.join()成功返回。
- 程序中断规则:对线程interrupted()方法的调用happens-before与被中断线程的代码检测到中断时间的发生。
- 对象finalize规则:一个对象初始化完成(构造函数执行结束)happens-before于发生它的finalize()方法的开始。
2.1 程序顺序规则(as-if-serial语义)
- 不能改变程序的执行结果(在单线程环境下,执行的结果不变)
- 依赖问题, 如果两个指令存在依赖关系,不允许重排序
这样听起来好像happens-before和as-if-serial语义没什么区别,但是一定要搞清楚as-if-serial是组成happens-before模型的其中一条规则,两者是包含的关系,下面对两者进行一个比较:
- as-if-serial语义保证单线程内程序的执行结果不被改变,happens-before保证正确同步的多线程和单线程的执行结果不被改变。
- as-if-serial语义给编写单线程程序的程序员创造了一个幻境:单线程程序是按程序的顺序来执行的。happens-before关系给编写正确同步的多线程程序的程序员创造了一个幻境:正确同步的多线程程序是按happens-before指定的顺序来执行的。
- as-if-serial语义和happens-before这么做的目的,都是为了在不改变程序执行结果的前提下,尽可能地提高程序执行的并行度。
int a=0;
int b=0;
void test(){int a=1; aint b=1; bint c=a*b; c
}
a happens -before b ; b happens before c
2.2 传递性规则
a happens-before b 且 b happens- before c,那么 a happens-before c
2.3 volatile变量规则
volatile 修饰的变量的写操作,一定happens-before后续对于volatile变量的读操作。该规则利用volatile关键字通过内存屏障机制来防止指令重排。
上图中的No表示不允许重排序,由上图我们就知道了使用volatile可以保证有序性。
public class VolatileExample {int a = 0;volatile boolean flag = false;public void writer() {a = 1; 1flag = true; // 修改 2}public void reader() {if (flag) { // true 3// i的值最终一定会被设置为1int i = a; 4}}
}
1 happens-before 2 -> 因为程序顺序规则和volatile规则。
3 happens-before 4 -> 因为程序顺序规则
2 happens-before 3 -> 因为volatile规则
1 happens-before 4 -> 因为传递性规则
基于上面的规则,保证了最终i=1成立。
这里重点讲一下上面的1 happens-before 2 为什么成立。在上面给出了volatile重排序规则表的图片,其中有一条是代码中第一个操作是普通读写(在上面代码中对应了a=1),第二个操作是volatile写(在上面代码中对应了flag=true),这种情况是不允许重排序的,所以1 happens-before 2成立。
2.4 监视器锁规则
int x = 10;
synchronized(this) {// 后续线程读取到的x的值一定12if(x < 12) {x = 12;}
}
x = 12;
2.5 start规则
public class StartDemo{int x = 0;Thread t1 = new Thread(()->{// 读取x的值 一定是20if(x == 20){}});x = 20;// t1.start()以及之前的所有操作都发生在t1线程中任意操作之前t1.start();
}
2.6 Join规则
public class Test{int x=0;Thread t1=new Thread(()->{// t1线程中的任意操作都发生在当前线程中t1.join成功返回之前x = 200;});t1.start();t1.join(); //保证结果的可见性。//在此处读取到的x的值一定是200.
}
相关文章:
【Java内存模型】Java内存模型(JMM)详解以及并发编程的三个重要特性(原子性,可见性,有序性)
【并发编程】volatile关键字最全详解,看这一篇就够了
【并发编程】synchronized关键字最全详解,看这一篇就够了
相关文章:

【并发基础】Happens-Before模型详解
目录 一、Happens-Before模型简介 二、组成Happens-Before模型的八种规则 2.1 程序顺序规则(as-if-serial语义) 2.2 传递性规则 2.3 volatile变量规则 2.4 监视器锁规则 2.5 start规则 2.6 Join规则 一、Happens-Before模型简介 除了显示引用vo…...

Kubernetes系列---Kubernetes 理论知识 | 初识
Kubernetes系列---Kubernetes 理论知识 | 初识 1.K8s 是什么?2.K8s 特性3.小拓展(业务升级)4.K8s 集群架构与组件①架构拓扑图:②Master 组件③Node 组件 五 K8s 核心概念六 官方提供的三种部署方式总结 1.K8s 是什么?…...

KingbaseES 原生XML系列三--XML数据查询函数
KingbaseES 原生XML系列三--XML数据查询函数(EXTRACT,EXTRACTVALUE,EXISTSNODE,XPATH,XPATH_EXISTS,XMLEXISTS) XML的简单使其易于在任何应用程序中读写数据,这使XML很快成为数据交换的一种公共语言。在不同平台下产生的信息,可以很容易加载XML数据到程序…...

【51单片机】点亮一个LED灯(看开发板原理图十分重要)
🎊专栏【51单片机】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【The Right Path】 🥰大一同学小吉,欢迎并且感谢大家指出我的问题🥰 目录 🍔基础内容 🏳…...

数据可视化工具 - ECharts以及柱状图的编写
1 快速上手 引入echarts 插件文件到html页面中 <head><meta charset"utf-8"/><title>ECharts</title><!-- step1 引入刚刚下载的 ECharts 文件 --><script src"./echarts.js"></script> </head>准备一个…...

【AI绘画】——Midjourney关键词格式解析(常用参数分享)
目前在AI绘画模型中,Midjourney的效果是公认的top级别,但同时也是相对较难使用的,对小白来说比较难上手,主要就在于Mj没有webui,不能选择参数,怎么找到这些隐藏参数并且触发它是用好Mj的第一步。 今天就来…...

操作符知识点大全(简洁,全面,含使用场景,演示,代码)
目录 一.算术操作符 1.要点: 二.负数原码,反码,补码的互推 1.按位取反操作符:~(二进制位) 2.原反补互推演示 三.进制位的表示 1.不同进制位的特征: 2.二进制位表示 3.整型的二进制表…...

华工研究生语音课
这门课讲啥 语音蕴含的信息、语音识别的目的 语音的准平稳性、分帧、预加重、时域特征分析(能量和过零率)、端点检测(双门限法) 语音的基频及检测(主要是自相关法、野点的处理) 声音的产生过程…...

KingbaseES 原生XML系列二 -- XML数据操作函数
KingbaseES 原生XML系列二--XML数据操作函数(DELETEXML,APPENDCHILDXML,INSERTCHILDXML,INSERTCHILDXMLAFTER,INSERTCHILDXMLBEFORE,INSERTXMLAFTER,INSERTXMLBEFORE,UPDATEXML) XML的简单使其易于在任何应用程序中读写数据,这使XML很快成为数据交换的一种公共语言。…...

【Flink】DataStream API使用之源算子(Source)
源算子 创建环境之后,就可以构建数据的业务处理逻辑了,Flink可以从各种来源获取数据,然后构建DataStream进项转换。一般将数据的输入来源称为数据源(data source),而读取数据的算子就叫做源算子(…...

树莓派硬件介绍及配件选择
目录 树莓派Datasheet下载地址: Raspberry 4B 外观图: 技术规格书: 性能介绍: 树莓派配件选用 电源的选用: 树莓派外壳选用: 内存卡/U盘选用 树莓派Datasheet下载地址: Raspberry Pi …...

O2OA (翱途) 平台 V8.0 发布新增数据台账能力
亲爱的小伙伴们,O2OA (翱途) 平台开发团队经过几个月的持续努力,实现功能的新增、优化以及问题的修复。2023 年度 V8.0 版本已正式发布。欢迎大家到 O2OA 的官网上下载进行体验,也希望大家在藕粉社区里多提宝贵建议。本篇我们先为大家介绍应用…...

数控解锁怎么解 数控系统解锁解密
Amazon Fargate 在中国区正式落地,因 数控解锁使用 Serverless 架构,更加适合对性能要求不敏感的服务使用,Pyroscope 是一款基于 Golang 开发的应用程序性能分析工具,Pyroscope 的服务端为无状态服务且性能要求不敏感,…...

3.0 响应式系统的设计与实现
1、Proxy代理对象 Proxy用于对一个普通对象代理,实现对象的拦截和自定义,如拦截其赋值、枚举、函数调用等。里面包含了很多组捕获器(trap),在代理对象执行相应的操作时捕获,然后在内部实现自定义。 const…...

Rust 快速入门60分① 看完这篇就能写代码了
Rust 一门赋予每个人构建可靠且高效软件能力的语言https://hannyang.blog.csdn.net/article/details/130467813?spm1001.2014.3001.5502关于Rust安装等内容请参考上文链接,写完上文就在考虑写点关于Rust的入门文章,本专辑将直接从Rust基础入门内容开始讲…...

【5.JS基础-JavaScript的DOM操作】
1 认识DOM和BOM 所以我们学习DOM,就是在学习如何通过JavaScript对文档进行操作的; DOM Tree的理解 DOM的学习顺序 DOM的继承关系图 2 document对象 3 节点(Node)之间的导航(navigator) 4 元素࿰…...

【大数据之Hadoop】二十九、HDFS存储优化
纠删码和异构存储测试需要5台虚拟机。准备另外一套5台服务器集群。 环境准备: (1)克隆hadoop105为hadoop106,修改ip地址和hostname,然后重启。 vim /etc/sysconfig/network-scripts/ifcfg-ens33 vim /etc/hostname r…...

SuperMap GIS基础产品组件GIS FAQ集锦(2)
SuperMap GIS基础产品组件GIS FAQ集锦(2) 【iObjects for Spark】读取GDB参数该如何填写? 【解决办法】可参考以下示例: val GDB_params new util.HashMapString, java.io.Serializable GDB_params.put(FeatureRDDProviderParam…...

C语言printf()函数中整型格式说明符详解
每个整型在printf()函数中对应不同的格式说明符,以实现该整型的打印输出。格式说明符必须使用小写。现在让我们看看各个整型及其格式说明符: 短整型(short) 10进制:%hd16进制:无负数格式,正数使用%hx8进制:无负数格式,正数使用%ho c short s 34; printf("%hd", s…...

阿里云服务器地域和可用区怎么选择合适?
阿里云服务器地域和可用区怎么选择?地域是指云服务器所在物理数据中心的位置,地域选择就近选择,访客距离地域所在城市越近网络延迟越低,速度就越快;可用区是指同一个地域下,网络和电力相互独立的区域&#…...

Java序列化引发的血案
1、引言 阿里巴巴Java开发手册在第一章节,编程规约中OOP规约的第15条提到: **【强制】**序列化类新增属性时,请不要修改serialVersionUID字段,避免反序列失败;如果完全不兼容升级,避免反序列化混乱&#x…...

为Linux系统添加一块新硬盘,并扩展根目录容量
我的原来ubuntu20.04系统装的时候不是LVM格式的分区, 所以先将新硬盘转成LVM,再将原来的系统dd到新硬盘,从新硬盘的分区启动,之后再将原来的分区转成LVM,在融入进来 1:将新硬盘制作成 LVM分区 我的新硬盘…...

树莓派Opencv调用摄像头(Raspberry Pi 11)
前言:本人初玩树莓派opencv,使用的是树莓派Raspberry Pi OS 11,系统若不一致请慎用,本文主要记录在树莓派上通过Opencv打开摄像头的经验。 1、系统版本 进入树莓派,打开终端输入以下代码(查看系统的版本&…...

国产ChatGPT命名图鉴
很久不见这般热闹的春天。 随着ChatGPT的威名席卷全球,大洋对岸的中国厂商也纷纷亮剑,各式本土大模型你方唱罢我登场,声势浩大的发布会排满日程表。 有趣的是,在这些大模型产品初入历史舞台之时,带给世人的第一印象其…...

操作系统——进程管理
0.关注博主有更多知识 操作系统入门知识合集 目录 0.关注博主有更多知识 4.1进程概念 4.1.1进程基本概念 思考题: 4.1.2进程状态 思考题: 4.1.3进程控制块PCB 4.2进程控制 思考题: 4.3线程 思考题: 4.4临界资源与临…...

第四十一章 Unity 输入框 (Input Field) UI
本章节我们学习输入框 (Input Field),它可以帮助我们获取用户的输入。我们点击菜单栏“GameObject”->“UI”->“Input Field”,我们调整一下它的位置,效果如下 我们在层次面板中发现,这个InputField UI元素包含两个子元素&…...

10.集合
1.泛型 1.1泛型概述 泛型的介绍 泛型是JDK5中引入的特性,它提供了编译时类型安全检测机制 泛型的好处 把运行时期的问题提前到了编译期间避免了强制类型转换 泛型的定义格式 <类型>: 指定一种类型的格式.尖括号里面可以任意书写,一般只写一个字母.例如:…...

强化学习p3-策略学习
Policy Network (策略网络) 我们无法知道策略函数 π \pi π所以要做函数近似,求一个近似的策略函数 使用策略网络 π ( a ∣ s ; θ ) \pi(a|s;\theta) π(a∣s;θ) 去近似策略函数 π ( a ∣ s ) \pi(a|s) π(a∣s) ∑ a ∈ A π ( a ∣ s ; θ ) 1 \sum_{a\in …...

初学Verilog语言基础笔记整理(实例点灯代码分析)持续更新~
实例:点灯学习 一、Verilog语法学习 1. 参考文章 刚接触Verilog,作为一个硬件小白,只能尝试着去理解,文章未完…持续更新。 参考博客文章: Verilog语言入门学习(1)Verilog语法【Verilog】一文…...

关于 std::condition_variable
一. std::condition_variable是什么? std::condition_variable 是 C 标准库提供的一个线程同步的工具,用于实现线程间的条件变量等待和通知机制。 条件变量的发生通常与某个共享变量的状态改变相关。 在多线程编程中,条件变量通常和互斥锁…...