摘要:在并发编程中,Happens-Before原则是我们必须要掌握的,今天我们就一起来详细聊聊并发编程中的Happens-Before原则。

本文分享自华为云社区《【高并发】一文秒懂Happens-Before原则》,作者:冰 河。

在并发编程中,Happens-Before原则是我们必须要掌握的,今天我们就一起来详细聊聊并发编程中的Happens-Before原则。

在正式介绍Happens-Before原则之前,我们先来看一段代码。

【示例一】

class VolatileExample {int x = 0;volatile boolean v = false;public void writer() {x = 42;v = true;}public void reader() {if (v == true) {//x的值是多少呢?}}
}

以上示例来源于:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#finalWrong

这里,假设线程A执行writer()方法,按照volatile会将v=true写入内存;线程B执行reader()方法,按照volatile,线程B会从内存中读取变量v,如果线程B读取到的变量v为true,那么,此时的变量x的值是多少呢??

这个示例程序给人的直觉就是x的值为42,其实,x的值具体是多少和JDK的版本有关,如果使用的JDK版本低于1.5,则x的值可能为42,也可能为0。如果使用1.5及1.5以上版本的JDK,则x的值就是42。

看到这个,就会有人提出问题了?这是为什么呢?其实,答案就是在JDK1.5版本中的Java内存模型中引入了Happens-Before原则。

接下来,我们就结合案例程序来说明Java内存模型中的Happens-Before原则。

【原则一】程序次序规则

在一个线程中,按照代码的顺序,前面的操作Happens-Before于后面的任意操作。

例如【示例一】中的程序x=42会在v=true之前执行。这个规则比较符合单线程的思维:在同一个线程中,程序在前面对某个变量的修改一定是对后续操作可见的。

【原则二】volatile变量规则

对一个volatile变量的写操作,Happens-Before于后续对这个变量的读操作。

也就是说,对一个使用了volatile变量的写操作,先行发生于后面对这个变量的读操作。这个需要大家重点理解。

【原则三】传递规则

如果A Happens-Before B,并且B Happens-Before C,则A Happens-Before C。

我们结合【原则一】、【原则二】和【原则三】再来看【示例一】程序,此时,我们可以得出如下结论:

(1)x = 42 Happens-Before 写变量v = true,符合【原则一】程序次序规则。

(2)写变量v = true Happens-Before 读变量v = true,符合【原则二】volatile变量规则。

再根据【原则三】传递规则,我们可以得出结论:x = 42 Happens-Before 读变量v=true。

也就是说,如果线程B读取到了v=true,那么,线程A设置的x = 42对线程B就是可见的。换句话说,就是此时的线程B能够访问到x=42。

其实,Java 1.5版本的 java.util.concurrent并发工具就是靠volatile语义来实现可见性的。

【原则四】锁定规则

对一个锁的解锁操作 Happens-Before于后续对这个锁的加锁操作。

例如,下面的代码,在进入synchronized代码块之前,会自动加锁,在代码块执行完毕后,会自动释放锁。

【示例二】

public class Test{private int x = 0;public void initX{synchronized(this){ //自动加锁if(this.x < 10){this.x = 10;}} //自动释放锁}
}

我们可以这样理解这段程序:假设变量x的值为10,线程A执行完synchronized代码块之后将x变量的值修改为10,并释放synchronized锁。当线程B进入synchronized代码块时,能够获取到线程A对x变量的写操作,也就是说,线程B访问到的x变量的值为10。

【原则五】线程启动规则

如果线程A调用线程B的start()方法来启动线程B,则start()操作Happens-Before于线程B中的任意操作。

我们也可以这样理解线程启动规则:线程A启动线程B之后,线程B能够看到线程A在启动线程B之前的操作。

我们来看下面的代码。

【示例三】

//在线程A中初始化线程B
Thread threadB = new Thread(()->{//此处的变量x的值是多少呢?答案是100
});
//线程A在启动线程B之前将共享变量x的值修改为100
x = 100;
//启动线程B
threadB.start();

上述代码是在线程A中执行的一个代码片段,根据【原则五】线程的启动规则,线程A启动线程B之后,线程B能够看到线程A在启动线程B之前的操作,在线程B中访问到的x变量的值为100。

【原则六】线程终结规则

线程A等待线程B完成(在线程A中调用线程B的join()方法实现),当线程B完成后(线程A调用线程B的join()方法返回),则线程A能够访问到线程B对共享变量的操作。

例如,在线程A中进行的如下操作。

【示例四】

Thread threadB = new Thread(()-{//在线程B中,将共享变量x的值修改为100x = 100;
});
//在线程A中启动线程B
threadB.start();
//在线程A中等待线程B执行完成
threadB.join();
//此处访问共享变量x的值为100

【原则七】线程中断规则

对线程interrupt()方法的调用Happens-Before于被中断线程的代码检测到中断事件的发生。

例如,下面的程序代码。在线程A中中断线程B之前,将共享变量x的值修改为100,则当线程B检测到中断事件时,访问到的x变量的值为100。

【示例五】

    //在线程A中将x变量的值初始化为0private int x = 0;public void execute(){//在线程A中初始化线程BThread threadB = new Thread(()->{//线程B检测自己是否被中断if (Thread.currentThread().isInterrupted()){//如果线程B被中断,则此时X的值为100System.out.println(x);}});//在线程A中启动线程BthreadB.start();//在线程A中将共享变量X的值修改为100x = 100;//在线程A中中断线程BthreadB.interrupt();}

【原则八】对象终结原则

一个对象的初始化完成Happens-Before于它的finalize()方法的开始。

例如,下面的程序代码。

【示例六】

public class TestThread {public TestThread(){System.out.println("构造方法");}@Overrideprotected void finalize() throws Throwable {System.out.println("对象销毁");}public static void main(String[] args){new TestThread();System.gc();}
}

运行结果如下所示。

构造方法
对象销毁

好了,今天就到这儿吧。我们下期见~~

点击关注,第一时间了解华为云新鲜技术~

8大原则带你秒懂Happens-Before原则相关推荐

  1. 大神带你秒懂Modbus通信协议

    摘要:昨天有小伙伴在群里问关于Modbus通信协议的,大家都比较积极地解答,所以今天果子哥总结一下关于Modbus相关的知识,适合正在入门的小伙伴"食用".同样还是理论+实战的方式 ...

  2. 【直播回顾】技术大咖带你学前端:走近前端设计模式

    本周白月光为我们带来了前端福音,4月8日晚上直播串讲了前端开发的相关知识,为了方便错过直播的小伙伴学习,本次直播是可以回放观看的~ [直播回放]前端技术变革(一):前端设计模式演变 https://l ...

  3. 王者荣耀10连胜,竟然也有人不相信,猎游大神带菜鸡玩家10连胜

    如果一局游戏一个人头都没有获得,甚至还送了几个人头,这一局游戏大家知道是胜利还是惨败?很多玩家都知道遇到这样的情况,这一局游戏基本上没有戏了,那就是根本不可能有太大的可能再获得胜利,特别是前期敌人顺风 ...

  4. 面象对象设计6大原则之二:开放封闭原则

    转载自 面象对象设计6大原则之二:开放封闭原则 开放封闭原则(OCP),The Open Closed Principle 定义 一个软件的实体,包括类.方法.模块.应该对扩展开放,对修改关闭. 也就 ...

  5. python查看数据大小_科多大数据带你看Python可以列为最值得学习的编程语言

    原标题:科多大数据带你看Python可以列为最值得学习的编程语言 不知道从什么时候开始,这句话开始流行.不过也从侧面反映出 Python 语言的特点:简单.高效. 从近期代表技术趋势的业界报告以及编程 ...

  6. 『教师节』程序猿用文心大模型带你一键加速祝福,祝老师们节日快乐

    『教师节』文心大模型带你一键加速祝福 文心大模型助力教师节 猜猜他谁 教师节&&中秋节 中秋助力 大模型使用指南 教师节助力 安装wenxin_api第三方库 定义我们的祝福主题与内容 ...

  7. 『中秋赏月』程序员用文心大模型带你玩转不一样的中秋

    『中秋赏月』文心大模型带你玩转不一样的中秋 中秋 文心大模型使用 安装wenxin_api 利用ERNIE-ViLG文生图模型生成图片 下载生成的图片 使用ERNIE 3.0模型,生成有关中秋的佳句 ...

  8. 带你认识六种设计原则(开闭原则、里氏代换原则、依赖倒转原则....)

    前言 1. 设计原则 1.1. 开-闭原则 1.2. 里氏代换原则 1.3. 依赖倒转原则 1.4. 接口隔离原则 1.5. 合成/聚合原则 1.6. 迪米特法则 前言 学习设计模式之前先要了解其中的 ...

  9. 重庆12岁男孩大毛带着7岁半的弟弟小毛逃学,跑到山里呆了13天

    因怕上学迟到被责骂,重庆12岁男孩大毛带着7岁半的弟弟小毛逃学,跑到山里呆了13天,饿了喝点泉水,冷了抱在一起.被找到时,兄弟俩双脚均已冻伤,肿得透明发亮.经送医救治诊断,大毛面临脚趾截肢的危险,兄弟 ...

最新文章

  1. Bash Shell脚本编程-变量知识
  2. c++经典书籍--Effective C++
  3. 《gcc五分钟系列》第六节:编译期符号检查
  4. 单体多字系统以及多体并行系统
  5. 终于有人把幸存者偏差讲明白了
  6. [C++] 栈 和 队列
  7. 从未在一起更让人遗憾_科比生涯4大遗憾,只有1个MVP,错过保罗,跟腱撕裂再无传奇...
  8. checkbox click和change事件
  9. C++刷题——2802: 推断字符串是否为回文
  10. 【渝粤教育】国家开放大学2018年秋季 0109-22T公司财务 参考试题
  11. wowza流媒体服务器安全设置
  12. kingbase 修改数据库密码
  13. 为什么python打不开_Python打不开.py文件
  14. 虚拟汽车加油问题 (贪心算法)
  15. 全国青少年软件编程等级考试标准 (预备级)1-4级
  16. linux还原环境,Linux环境利用恢复被rm意外删除数据文件
  17. 第八期杭州NodeParty x Rokid技术分享会回顾
  18. HDU 6194 string string string (后缀数组+线段树)
  19. php 计算函数 (加,比较用法,除,减,求余,乘)
  20. Windows11安装方法(预览体验升级)

热门文章

  1. android 开源_Android开源如何使其成为移动市场的领导者
  2. (22)HTML Emoji
  3. (10)<label>标签的用途
  4. HTML5 保存画布
  5. c++里面的内联函数
  6. verp中的redundantRobot的逆运动学注意事项
  7. 深度学习笔记(30) Inception网络
  8. [Tips] WSL ubuntu 18.04 安装python3
  9. ThreadPoolExecutor解析
  10. MySQL.Linux.安装