JMM之先行发生原则(happens-before)详解
1、概述
在JMM规范下,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happers-before(先行发生)原则。
例如
int x = 10 ;
int y = x;
这两行代码中第二个操作 y=x ,因为需要将x值赋值给y,所以第一行代码的结果需要对第二行代码可见。也就是第一行代码happers-before第二行代码。
有些朋友看了可能觉得,作者说的不是废话嘛,这两行代码肯定是先执行第一行再执行第二行。答案肯定是的,因为y的值依赖x的值,这属于JMM规范happens-before原则内的,所以会按照代码编写顺序执行。假如没有happens-before原则,则y的值可能就不会等于10,可见happens-before原则的重要性。
happens-before原则是判断数据是否存在竞争,线程是否安全的非常有用的手段。依赖这个原则,我们可以通过几条简单的规则,解决并发环境下两个操作之间是否可能存在冲突的所有问题,而不需要陷入Java内存模型苦涩难懂的底层编译原理之中。
那接下来我们就说说happens-before都有那些具体原则。
2、happens-before具体原则
- 次序规则
一个线程内,按照代码顺序,写在前面的操作先行发生于写在后面的操作。
注意:这里是一个线程内,如果是多个线程这不属于happens-before原则
int x = 10;
int y = 20;
在一个线程内 在执行y=20 时,x肯定是10;但是如果在多线程下由于编译器优化,指令重排序 在执行y=20时,x可能等于0
- 锁定规则
一个unlock操作先行发生于后面(这里的”后面“是指时间上的先后)对同一把锁的lock操作;
static Lock lock = new ReentrantLock();public static void main(String[] args) {lock.lock();try {System.out.println("------锁定1");} finally {//解锁lock.unlock();}//再次锁定 第二次加锁操作肯定是发生在上一次解锁 之后lock.lock();try {System.out.println("------锁定2");} finally {lock.unlock();}}
- volatile变量规则
对一个volatile变量的写操作先行发生于后面对这个变量的读操作,前面的写对后面的读是可见的,这里的”后面“也是指时间上的先后。
咱们都知道被volatile修饰的变量,拥有JMM三大特性的可见性和有序性。对于同一个共享volatile变量,线程A修改了变量值,则其他线程立马就可以看到。
static volatile Boolean flag = false;public static void main(String[] args) {new Thread(() -> {if (!flag) {System.out.println("--正常运行");}System.out.println("--终止");}, "t1").start();new Thread(() -> {//更改volatile 变量值,t1线程立马就能收到flag = true;System.out.println("终止线程t1");}, "t2").start();}
看到这里是不是就知道了,为啥 volatile 修饰变量,变量具有可见性和有序性,其实底层是happens-before原则制定的。
- 传递性规则
如果操作A先行发生于操作B,操作B先行发生于操作C,那么操作A肯定先行发生于C。
- 线程启动规则
Thread对象的start()方法先行发生于此线程中执行的所有代码。
public static void main(String[] args) {Thread t1 = new Thread(() -> {/*** 线程内操作 后执行*/System.out.println("线程执行内容1");System.out.println("线程执行内容2");}, "t1");// start()方法先执行t1.start();}
- 线程中断规则
对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()或者isInterrupted()检测到是否有中断发生。
Thread t1 = new Thread(() -> {//检查线程中断标识位是否被设置为truewhile (!Thread.currentThread().isInterrupted()) {System.out.println("---正常执行");}System.out.println("被中断");}, "t1");//线程1启动t1.start();new Thread(() -> {System.out.println("将t1 线程中断标识位设置为true");t1.interrupt();}, "t2").start();
---正常执行
.....
.....
---正常执行
---正常执行
---正常执行
---正常执行
将t1 线程中断标识位设置为true
---正常执行
被中断
- 线程终止规则
线程中的所有操作都先行发生于对此线程的终止检测,可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测到线程已经终止执行。
- 对象终结规则
一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。
这条规则指出,一个对象在被垃圾回收之前必须已经进过初始化,垃圾回收不可能也不能去回收一个根本不存在的对象。
3、总结
如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则制定的顺序来执行。如果重排序之后的执行结果与按照happens-before关系来执行的结果一致,那么这种重排序是可以正常执行的。
相关文章:

JMM之先行发生原则(happens-before)详解
1、概述 在JMM规范下,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happers-before(先行发生)原则。 例如 int x 10 ; int y x; 这两行代码中第二个操作 yx ,因为需要将x值赋值给y,所以第一行代码的…...

含分布式电源的配电网可靠性评估研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

安全加固服务是什么?哪些行业需要做?
安全加固服务是什么?安全加固服务是一种针对企业信息系统、网络设备、应用程序等进行安全加固和优化的服务。安全加固服务的主要目的是保障企业信息系统的安全性和稳定性,有效防范各类网络攻击和安全威胁。 安全加固服务是什么?通常包括以下…...

好程序员:Java书籍推荐,程序员必看的5本Java书籍,赶紧收藏!
今天好程序员给大家推荐5本Java书籍,各大高校都在使用(具体名单如下),所有学习Java的程序员都不应该错过! 第一本Java书籍《Java EE(SSM框架)企业应用实战》 本书全面介绍了JavaEE中MyBatis、Sp…...

maven将jar包添加到本地仓库
第一步:下载需要添加的jar包 可以在maven库中查找下载,也可以在对应官网下载 maven库网址:https://mvnrepository.com/ 找到对应版本的jar包下载 第二步:将下载的jar包放到指定位置(位置自己指定)…...

4.12--计算机网络之TCP篇之TCP 协议的缺陷+如何基于 UDP 协议实现可靠传输?--(复习+大总结)---沉下心来(加油呀)
TCP 协议四个方面的缺陷: 1.升级 TCP 的工作很困难; TCP 协议是在内核中实现的,应用程序只能使用不能修改,如果要想升级 TCP 协议,那么只能升级内核。 而升级内核这个工作是很麻烦的事情 2.TCP 建立连接的延迟&#x…...

数据库网络编程
数据库网络编程是一个重要的领域,它涉及到如何使用编程语言与数据库进行交互,以及如何设计和实现网络应用程序。在这篇文章中,我将探讨数据库网络编程的基础知识、常用技术和实践经验,以及一些应用案例和未来发展趋势。 一、基础…...

为什么现代企业都在使用ERP系统 它有哪些优势
随着科技的不断发展,企业管理方式也在不断地发生改变。在这个信息化的时代,企业要想取得成功,必须要善于利用先进的信息化技术工具。其中,ERP系统是企业管理中不可或缺的重要工具。本文将探讨现代企业为什么会使用ERP系统…...

别再用 BeanUtils 了,这款 PO VO DTO 转换神器不香么?
老铁们是不是经常为写一些实体转换的原始代码感到头疼,尤其是实体字段特别多的时候。介绍一个开源项目 mapstruct ,可以轻松优雅的进行转换,简化你的代码。当然有的人喜欢写get set,或者用BeanUtils 进行复制,代码只是…...

LeetCode算法小抄-- 最近公共祖先 和 完全二叉树的节点个数
LeetCode算法小抄-- 最近公共祖先 和 完全二叉树的节点个数 最近公共祖先[236. 二叉树的最近公共祖先](https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/)[235. 二叉搜索树的最近公共祖先](https://leetcode.cn/problems/lowest-common-ancestor-of-a-b…...

php、redis实现分布式锁的正确写法(原子操作 通用类 加讲解)
最终代码(通用类) 1 面试中、实际工作中,经常涉及到 redis 分布式锁,正确写法如下。先奉上代码,再讲解。 <?php namespace app\common\library; /*** 通用分布式锁(原子操作)*/ class Lock {/*** 获取redis实例* return \Redis* throws…...

Transformer在时序预测的应⽤第一弹——Autoformer
Transformer在时序预测的应⽤第一弹——Autoformer 原文地址:Autoformer: Decomposition Transformers with Auto-Correlation for Long-Term Series Forecasting(NIPS 2021) 做长时间序列的预测 Decomposition把时间序列做拆分,…...

文章改写神器在线-AI续写文章生成器
AI续写生成器 AI续写生成器是一种利用人工智能技术的创意工具,能够提高写作效率,为营销推广带来全新的可能性。无论你是写手、广告人员还是市场营销人员,这个工具都能够有效地解决你在写作中遇到的难题。 在内容创作行业中,原创…...

一秒钟给硬盘文件做个树状结构目录
一秒钟给硬盘文件做个树状结构目录 一、背景 对于长时间坐在电脑前的打工人来说,若没有养成良好文件分类习惯的话,年终整理电脑文件绝对是件头疼的事情。 给磁盘文件做个目录,一目了然文件都在哪里?想想都是件头疼的事情。 对于…...

电脑重装系统后会怎样?
有小伙伴的电脑系统运行缓慢卡顿,现在想通过重装系统来解决问题。咨询电脑重装系统会怎么样对系统有影响吗,现在小编就带大家看看电脑重装系统后会怎样。 方法/步骤: 一、电脑重装系统会怎么样 1、我们的电脑重装系统后,电脑…...

100种思维模型之反熵增思维模型-47
查理芒格被誉为反熵增思维模型的倡导者。本文将介绍查理芒格的反熵增思维模型,并分析它的实用性。 一、什么是熵增? 在物理学中,熵是衡量系统无序程度的指标。系统的熵越高,其无序程度越高。这个概念也可以应用到其他领域。在金融…...

【网络安全】Xss漏洞
xss漏洞 xss漏洞介绍危害防御方法xss测试语句xss攻击语句1. 反射性xss2.存储型xss3.DOM型xssdvwa靶场各等级渗透方法xss反射型(存储型方法一致)LowMediumHightimpossible Dom型LowMediumHight xss漏洞介绍 定义:XSS 攻击全称跨站脚本攻击&am…...

17.网络爬虫—Scrapy入门与实战
这里写目录标题 Scrapy基础Scrapy运行流程原理Scrapy的工作流程Scrapy的优点 Scrapy基本使用(豆瓣网为例)创建项目创建爬虫配置爬虫运行爬虫如何用python执行cmd命令数据解析打包数据打开管道pipeline使用注意点 后记 前言: 🏘️🏘️个人简介…...

【面试题】JavaScript 中 try...catch 的使用技巧 ?
大厂面试题分享 面试题库 前后端面试题库 (面试必备) 推荐:★★★★★ 地址:前端面试题库 web前端面试题库 VS java后端面试题库大全 作为一位 Web 前端工程师,JavaScript 中的 try...catch 是我们常用的特性之一。…...

Java 命名格式规范
Java 命名格式规范 概述 简洁清爽的代码风格应该是大多数开发工程师所期待的。在编码过程中笔者常常因为起名字而纠结,夸张点可以说是编程 5 分钟,命名两小时!究竟为什么命名成为了编码中的拦路虎。 每个公司都有不同的标准,目…...

【C++】STL中的容器适配器 stack queue 和 priority_queue 的模拟实现
STL中的容器适配器 一、容器适配器1、什么是容器适配器2、STL标准库中的容器适配器 二、stack的模拟实现1、stack的简单介绍2、栈的模拟实现 三、queue的模拟实现1、queue的简单介绍2、queue的模拟实现 四、priority_queue的模拟实现1、priority_queue的简单介绍2、priority_qu…...

MongoDB 聚合管道中使用算术表达式运算符
算术表达式运算符主要用于实现数字之间的算术运算,主要包含了对加、减、乘、除、余数、截取、舍入等算术操作。 下面我们进行详细介绍: 一、准备数据 初始化商品数据 db.goods.insertMany([{ "_id": 1, name: "薯片", size: &q…...

代码随想录算法训练营第四十三天-动态规划5|1049. 最后一块石头的重量 II , 494. 目标和 , 474.一和零
最后一块石头重量转化为将一个集合分隔成两个集合,两个集合之间的差值最小,就是最后剩下最小的石头重量。这里可以求集合的一个平均值,如果正好等于平均值,说明可以抵消,这时候重量为0,如果不行,…...

《淘宝网店》:计算总收益
目录 一、题目 二、思路 1、当两个年份不一样的时候 (1)from年剩余之后的收益 (2)中间年份的全部收益 (3)to年有的收益 2、同一个年份 三、代码 详细注释版本: 简化注释版本ÿ…...

2023年03月青少年软件编程C语言一级真题答案——持续更新.....
1.字符长方形 给定一个字符,用它构造一个长为4个字符,宽为3个字符的长方形,可以参考样例输出。 时间限制:1000 内存限制:65536 输入 输入只有一行, 包含一个字符。 输出 该字符构成的长方形,长4个字符,宽3个字符。 样例输入 * 样例输出 **** **** ****#include<bi…...

家用洗地机好用吗?好用的洗地机分享
洗地机是一种高效、节能、环保的清洁设备,广泛应用于各种场所的地面清洁工作。它不仅可以快速清洁地面,还可以有效去除污渍、油渍等难以清洁的污染物,让地面恢复光洁如新的状态。同时,洗地机还可以减少清洁人员的劳动强度…...
《分解因数》:质因数分解
目录 一、题目: 二、思路: 三、代码: 一、题目: 分解因数 《分解因数》题目链接 所谓因子分解,就是把给定的正整数a,分解成若干个素数的乘积,即 a a1 a2 a3 ... an,并且 1 < a1…...

(排序10)归并排序的外排序应用(文件排序)
TIPS 在一些文件操作函数当中,fputc与fgetc这两个函数都是针对字符的,如果说你需要往文件里面去放入整形啊等等,不是字符的类型,这时候就用fprintf,fscanf在参数里面数据类型控制一下就可以。但是话说回来,…...

浅谈根号分治与分块
文章目录 1. 根号分治哈希冲突 2. 线性分块引入教主的魔法[CQOI2011] 动态逆序对[国家集训队] 排队[HNOI2010] 弹飞绵羊蒲公英 1. 根号分治 哈希冲突 题目1 n n n 个数, m m m 次操作。操作 1 为修改某一个数的值,操作 2 为查询所有满足下标模 x x x …...

(OpenAI)ChatGPT注册登录常见问题错误代码及其解决方法
在使用 ChatGPT 的时候我们可能会碰到一些错误的代码,本文统一来介绍一下每一种错误以及解决方法。 错误代码1. 不能在当前国家使用 出现场景:一般在注册或登录的时候会出现。 原因:主要是ChatGPT检测到当前访问所在的地区不允许访问导致。 …...