[转帖]Java Happens-Before原则

java,happens,before,原则 · 浏览次数 : 0

小编点评

**目录一、线程中的顺序性原则** * 在同一个线程中,按照程序的顺序,前面的操作Happens-Before后面的操作。 * 例如:`int a = 1; if(a == 1){ }` **目录二、volatile变量原则** * 在对于用volatile修饰的变量,它的写操作Happens-Before后续对它的读操作。 * 由于volatile变量的内存访问是线程安全的,所以写操作必须先执行完成,才能影响读操作。 **目录三、传递性** * 如果A Happens-Before B,且B Happens-Before C,那么A Happens-Before C。 * 例如:`volatile boolean a = false; int b = 1; if(a){ System.out.println(b); }` **目录四、管程中锁的原则** * 对同一个锁的解锁Happens-Before后续对这个锁的加锁。 * 例如:`int a = 1; synchronized (this){ if(a == 1){ a = 2; } }` **目录五、线程start原则** * 线程开始执行时,主线程能够看到子线程的操作。 * 例如:`int a = 1; Thread B = new Thread(() -> { a = 2; }); B.start();` **目录六、线程join原则** * 当子线程B完成后(主线程A中join()方法返回),主线程能够看到子线程的操作。 * 例如:`int a = 1; Thread B = new Thread(() -> { a = 2; }); B.start(); B.join(); `

正文


目录

作者:@dwtfukgv
本文为作者原创,转载请注明出处:https://www.cnblogs.com/dwtfukgv/p/15631282.html

Java Happens-Before原则

Happens-Before原则核心就是表达在一些条件下,前面的操作对于后面的操作是可见的。它有六个条件,或者说是六条原则。

一、线程中的顺序性原则

这个最容易理解,这个原则是指在同一个线程中,按照程序的顺序,前面的操作Happens-Before后面的操作。也就说在同一个线程中,前面先修改的数据,对于后面的操作是可见的。下面举例说明:

  int a = 1;
  if(a == 1){
   
  }

上面代码中第一行对变量a进行的修改Happens-Before第二行查询a的操作,所以第二行a == 1true

二、volatile变量原则

这个原则是指在对于用volatile修饰的变量,它的写操作Happens-Before后续对它的读操作。这个原则就是说在一个线程中先对volatile变量进行了修改后,在另一个线程中读取该volatile变量能够读到新值(因为CPU缓存的存在,可能在一个线程中修改了普通变量,另一个线程无法感知)。当然如果在同一个线程中,就可以直接使用原则一了。

  volatile int a = 1;
   
  // 线程A
  a = 2;
   
  // 线程B
  if(a == 2){
   
  }

上面代码中,假设有两个线程并发执行,线程A先对a进行赋值操作(第四行),然后线程B立刻读取a(第七行),此时能够保证读取的a一定是2。

三、传递性

这个原则是指Happens-Before原则是可以传递的,指如果A Happens-Before B,且B Happens-Before C,那么A Happens-Before C。

  volatile boolean a = false;
  int b = 1;
   
  // 线程A
  b = 2;
  a = true;
   
  // 线程B
  if(a){
  System.out.println(b);
  }
   

上面代码中,线程A先对b进行赋值,然后对a进行赋值,然后线程B读取a后,然后执行打印b的值(根据原则二a一定为true),b的值一定为2。因为根据原则一,对b赋值Happens-Before对a赋值,再根据原则二,对a的赋值Happens-Before对a的读取,最后再根据原则三,对b的赋值Happens-Before对b的读取的,所以b一定为2。

四、管程中锁的原则

这个原则是指对同一个锁的解锁Happens-Before后续对这个锁的加锁

  int a = 1;
   
  // 线程执行
  synchronized (this){
  if(a == 1){
  a = 2;
  }
  }

上面代码中,线程A先对a进行赋值,然后解锁,线程B再加锁后,此时读到的a一定为2。

五、线程start原则

这条原则是关于线程启动的。它是指主线程A启动子线程B后,子线程B能够看到主线程在启动子线程B前的操作。也就是说如果线程A调用线程B的 start() 方法(即在线程A中启动线程B),那么该start()操作 Happens-Before 于线程B中的任意操作。

  int a = 1;
  Thread B = new Thread(() -> {
  // 读取a
  });
  a = 2; // 假设能修改
  B.start();

上面代码中,线程A先对a进行赋值,然后再启动线程B,所以线程B如果能够读取a的话(当然int类型,java中是不允许的),那么a一定为2。

六、线程join原则

这条是关于线程等待的。它是指主线程A等待子线程B完成(主线程A通过调用子线程B的join()方法实现),当子线程B完成后(主线程A中join()方法返回),主线程能够看到子线程的操作

  int a = 1;
  Thread B = new Thread(() -> {
  // 修改a
  a = 2; // 假设能修改
  });
  B.start();
  B.join();
  // 读取a
   

上面代码中,读到的a一定为2。也就是说线程B中的操作对于B.join()后面的操作都是可见的。

与[转帖]Java Happens-Before原则相似的内容:

[转帖]Java Happens-Before原则

目录 一、线程中的顺序性原则 二、volatile变量原则 三、传递性 四、管程中锁的原则 五、线程start原则 六、线程join原则 作者:@dwtfukgv本文为作者原创,转载请注明出处:https://www.cnblogs.com/dwtfukgv/p/15631282.html Java

[转帖]Java 近期新闻:JEP 更新,GraalVM 贡献给 OpenJDK,JavaOne 重启

https://www.infoq.cn/article/kzzbQg5zgissaCcJlfey JEP 432记录模式(第二预览版)在上周从其 8294078 草案晋升为候选状态。相比 JEP 405 记录模式(预览版),该 JEP 更新了:对通用记录模式类型参数推断的支持、新增对记录模式出现在

[转帖]Java中线程的生命周期

https://blog.51cto.com/u_15773567/5832430 1 介绍 本篇文章我们讨论下Java中的一个非常核心的概念:线程的生命周期。在Java编程语言中,多线程编程非常重要。线程从创建到销毁是有生命周期的,在线程的生命周期中,线程会经历多种状态(state)。 2 线程状

[转帖]Java调优系列之工具篇之btrace、gperftools

https://github.com/landon30/Bulls/wiki/java-profiling-tools landon 网络游戏资深服务器架构师 2018-06-14 线上遇到了问题? 服务上线出问题,想增加打印日志怎么办? 线上怀疑某个接口慢,想打印接口耗时怎么办? 线上某个接口报错

[转帖]Java游戏服务器调优实践

https://www.jianshu.com/p/344f8141b63e Java Profiling Practice landon资深网络游戏服务器架构师 系统性能定义 Throughput 吞吐量,也就是每秒钟可以处理的请求数,任务数 Latency 系统延迟,也就是系统在处理一个请求或一

[转帖]java中方法不要写太长的真正原因

https://www.iteye.com/blog/enetor-976070 java中一般建议一个方法不要写的过长,不方便维护和阅读是其中的一个原因,但是其真正性能的原因大家知道吗? 我们知道,JVM一开始是以解释方式执行字节码的。当这段代码被执行的次数足够多以后,它会被动态优化并编译成机器码

[转帖]Java方法的JIT编译

https://www.jianshu.com/p/a6275e239eac Java方法执行一般会利用分层编译,先通过c1解释执行。方法执行编译等级逐渐提升,有机会通过JIT编译为特定平台汇编执行,以此获得最好的性能。 方法执行除了达到一定热度外,是否JIT编译也受到以下两个参数影响: -XX:+

[转帖]java profile配置

[转帖]JAVA 编程规范之建表规约

1、【强制】表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint( 1 表示是,0 表示否)。 2、【强制】表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名

[转帖]Java使用火焰图查看系统瓶颈

场景 一般情况下,我们会对现有系统进行压测等方式,来了解系统最大的吞吐量等等,通过这种方式得知系统在生产环境下可扛住的压力,如果我们想了解在压测的链路过程中,是哪些地方执行时间过长,影响了系统的吞吐量,可以使用火焰图的方式来观察。 工具 生成火焰图需要两个工具: 1. async-profiler: