0
点赞
收藏
分享

微信扫一扫

并发理论:Java如何解决可见性和有序列性


并发理论:Java如何解决可见性和有序列性_重排序

解决重排序引起的有序性

在之前的章节中,我们分析到基于执行效率的考虑,代码会被重排序

并发理论:Java如何解决可见性和有序列性_重排序_02


重排序会经历如下几个过程

并发理论:Java如何解决可见性和有序列性_数据_03


当然重排序也不是随便排,需要遵循as-if-serial原则,即不管怎么重排序(单线程)程序的执行结果不能被改变。编译器,runtime和处理器都必须遵循as-if-serial语义

例如如下一段代码

double pi = 3.14 // a
double r = 1.0 // b
double area = pi * r * r // c

数据之间的依赖关系如下所示

并发理论:Java如何解决可见性和有序列性_java_04


A和B之间并没有依赖关系,因此可以执行重排序

并发理论:Java如何解决可见性和有序列性_开发语言_05


重排序除了需要遵循as-if-serial原则(用于单个线程),Java还定义了happens-before原则也需要遵循(用于单个线程和多个线程)

两个操作之间具有happens-before关系,并不意味着前一个操作必须妖在后一个操作之前执行!happens-before仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前

如下为java中定义的8种happens-before原则

  1. 一个线程中的每个操作happens-before于该线程中的任意后续操作
  2. 对一个锁的解锁,happens-before于随后对这个锁的加锁
  3. 对一个volatile域的写,happens-before于任意后续对这个volatile域的读
  4. 如果A hapens-before B,且B happens-before C, 那么A happens-before C
  5. 如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before 于线程B中的任意操作
  6. 如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before 于线程A从ThreadB.join()操作成功返回
  7. 对线程interrup()方法的调用, happens-before 于被中断线程的代码检测到中断事件的发生
  8. 一个对象的初始化完成(构造函数执行结束)happens-before finalize()方法的开始

解决缓存引起的可见性

在Java中无论是锁还是volatile等,都是通过内存屏障来实现可见性的。

在Java中有如下四种屏障类型

屏障类型

指令示例

说明

LoadLoad Barriers

Load1; LoadLoad; Load2

确保Load1数据的装载先于Load2及所有后续装载指令的装载

StoreStore Barriers

Store1; StoreStore; Store2

确保Store1数据对其他处理器可见(刷新到内存)先于Store2及所有后续存储指令的存储

LoadStore Barriers

Load1; LoadStore; Store2

确保Load1数据装载优先于Store2及所有后续的存储指令刷新到内存

StoreLoad Barriers

Store1; StoreLoad; Load2

确保Store1数据对其他处理器变得可见(只刷新到内存)先于Load2及所有后续装载指令的装载

并发理论:Java如何解决可见性和有序列性_重排序_06

内存屏障主要有如下两个作用

  1. 阻止屏障两侧的指令重排序
  2. 强制将内存中的数据写回主内存

对于Load Barrier来说,在指令前插入Load Barrier,可以让高速缓存中的数据失效,强制从新从主内存加载数据

对于Store Barrier来说,在指令后插入Store Barrier,能让写入缓存中的最新数据更新写入主内存,让其他线程可见

参考博客


[2]https://www.jianshu.com/p/1508eedba54d/


举报

相关推荐

0 条评论