首页 > 编程语言 >Java的先行发生原则

Java的先行发生原则

时间:2023-06-05 11:47:26浏览次数:44  
标签:Java Thread 原则 发生 线程 先行 操作

先行发生原则(Happens-Before)是Java内存模型定义的一个等效判断原则。依赖这个原则,我们可以通过几条简单规则判断数据是否存在竞争,线程是否安全,而不需要陷入Java内存模型苦涩难懂的定义之中。


“先行发生”原则指的是什么。比如说操作 A 先行发生于操作 B,其实就是说在操作 B 发生之前,操作 A 产生的影响能被操作 B 观察到,“影响”包括修改了内存中共享变量的值、发送了消息、调用了方法等。

下面是Java内存模型下一些“天然的”先行发生关系,这些先行发生关系无须任何同步器协助就已经存在,可以在编码中直接使用。如果两个操作之间的关系不在此列,并且无法从下列规则推导出来,则这两个操作就没有顺序性保障,虚拟机可以对它们进行重排序。

  • 程序次序规则(Program Order Rule):在一个线程内,按照控制流顺序,书写在前面的操作先行发生于书写在后面的操作。注意,这里说的是控制流顺序而不是程序代码顺序,因为要考虑分支、循环等结构。
  • 管程锁定规则(Monitor Lock Rule):一个 unlock 操作先行发生于后面对同一个锁的 lock 操作。 这里必须强调的是“同一个锁”,而“后面”是指时间上的先后。
  • volatile 变量规则(Volatile Variable Rule):一个对 volatile 变量的写操作先行发生于后面对这个 volatile 变量的读操作,这里的“后面”同样是指时间上的先后。
  • 线程启动规则(Thread Start Rule):Thread 对象的 start() 方法先行发生于此线程的每一个动作。
  • 线程终止规则(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的终止检测。我们可以通过 Thread::join() 方法是否结束、Thread::isAlive() 的返回值等手段检测线程是否已经终止执行,这就算是对此线程的终止检测。
  • 线程中断规则(Thread Interruption Rule):Thread 对象的 interrupt() 方法先行发生于被中断线程的中断检测。我们可以通过 Thread::interrupted() 方法检测到是否有中断发生,这就算是被中断线程的中断检测。
  • 对象终结规则(Finalizer Rule):一个对象的初始化完成(构造函数执行结束) 先行发生于它的 finalize() 方法的开始。
  • 传递性(Transitivity):如果操作 A 先行发生于操作 B,操作 B 先行发生于操作 C,那就可以得出结论:操作 A 先行发生于操作 C。

Java 语言无须任何同步手段保障就能成立的先行发生规则有且只有上面这些。


“时间上的先后顺序”与“先行发生”之间有什么不同?

  • 一个操作“时间上的先发生”不代表这个操作会是 “先行发生”。譬如:操作 A 是 ,线程 A 对未被 volatile 修饰的共享变量的写操作;操作 B 是,线程 B 对该共享变量的读操作。在时间上,操作 A 比操作 B 先发生,但是不代表操作 A 先行发生于操作 B。
  • 如果一个操作“先行发生”,也不能推导出这个操作必定是“时间上的先发生”。譬如:程序次序规则,在一个线程内,按照控制流顺序,书写在前面的操作 A 先行发生于书写在后面的操作 B。但是,不代表在时间上,操作 A 比操作 B 先发生。普通的变量会保证在方法的执行过程中,所有依赖赋值结果的地方都能获取到正确的结果,而不能保证变量赋值操作的顺序与程序代码中的执行顺序一致。

时间先后顺序与先行发生原则之间基本没有因果关系,所以我们衡量并发安全问题的时候不要受时间顺序的干扰,一切必须以先行发生原则为准。

参考资料

《深入理解Java虚拟机》第 12 章:Java 内存模型与线程 12.3 Java 内存模型

02 | Java内存模型:看Java如何解决可见性和有序性问题 (geekbang.org)

标签:Java,Thread,原则,发生,线程,先行,操作
From: https://www.cnblogs.com/feiyu2/p/17457370.html

相关文章

  • java 内存模型
    java内存模型(memorymodel)定义了java虚拟机如何与计算机内存交互。JVM将内存主要分为栈(stack)内存和堆(heap)内存。每当我们声明新的变量和对象、调用新的方法、声明String或执行类似的操作时,JVM都会从堆栈内存或堆空间为这些操作指定内存。每个线程有自己的栈内存,是线程独有的。堆......
  • java 自己写分页
    longcurrent=1;longsize=100;StringcurrentOther=jsonObject.getStr("current");if(StrUtil.isNotBlank(currentOther)){//?:0或1个,*:0或多个,+:1或多个BooleanstrResult1=currentOther.matches("^[-\\+]?([0-9]+\\.?)?[0-9]+$");if......
  • Java中Double除保留后小数位的几种方法
     返回double型的1.能四舍五入doubled=114.145;d=(double)Math.round(d*100)/100;System.out.println(d); 2.BigDecimal.ROUND_HALF_UP表示四舍五入,BigDecimal.ROUND_HALF_DOWN也是五舍六入,BigDecimal.ROUND_UP表示进位处理(就是直接加1),BigDecimal.ROUND_DOWN表示直接......
  • Java体系面试题(2022)(一)
    全部试题及答案下载基础篇1、Java语言有哪些特点1、简单易学、有丰富的类库2、面向对象(Java最重要的特性,让程序耦合度更低,内聚性更高)3、与平台无关性(JVM是Java跨平台使用的根本)4、可靠安全5、支持多线程2、面向对象和面向过程的区别面向过程:是分析解决问题的步骤,然后用......
  • Java体系面试题(2022)(二)
    全部试题及答案下载JVM篇1、知识点汇总JVM是Java运行基础,面试时一定会遇到JVM的有关问题,内容相对集中,但对只是深度要求较高.其中内存模型,类加载机制,GC是重点方面.性能调优部分更偏向应用,重点突出实践能力.编译器优化和执行模式部分偏向于理论基础,重点掌握知识点.需了......
  • java 行转列
    行转列工具类publicclassRowConvertColUtil{privatestaticfinalStringNULL_VALUE="";privatestaticfinalStringHEADER_NULL_VALUE="工序";privatestaticSet<Object>headerSet;privatestaticSet<Object>f......
  • Java:从单线程计数器到多线程数据同步synchronized和原子类Atomic
    (目录)使用单线程单线程修改计数器的值,没有发生问题,每次运行结果都是10000,不过程序耗时较长packagecom.example;/***计数器*/classCounter{privatestaticlongcount;publicstaticlonggetCount(){returncount;}publicstaticv......
  • 基于JAVA操作系统在线网站SQL
    随着21世纪的到来,人们更深刻的感受到了计算机在生活和工作中作用的重要,越来越多的职业需要具有计算机的应用技能。掌握计算机是职业的需要,社会的需要,更是事业发展的需要。今天,计算机技术不但广泛地应用在办公自动化中,还全面参与到各行各业。所有与计算机相关的职业都要求工作者有......
  • Java学习笔记(十四)
    1.请描述你理解的IO流的作用 I/O流(输入/输出流)的作用是在程序与外部世界(例如文件、网络、控制台等)之间传输数据。2.请描述I/O流的体系结构(1)InputStream类和OutputStream类,其实现类:FileInputStream和FileOutputStream(2)Reader类和Writer类,其实现类:FileReader和FileWriter(3)缓......
  • Java Map 集合类简介
    源:http://www.oracle.com/technetwork/cn/articles/maps1-100947-zhs.html#T1评:了解最常用的集合类型之一Map的基础知识以及如何针对您应用程序特有的数据优化Map。java.util中的集合类包含Java中某些最常用的类。最常用的集合类是List和Map。......