重排序与happens-before介绍

~本文小结:

  1. 重排序能减少CPU流水线处理的中断,提高性能;
  2. 重排序分为三类:编译器层面,指令层面和内存层面;
  3. 顺序一致性模型指天然“同步且有原子性”的模型,其执行顺序为代码顺序;
  4. JMM保证正确同步的程序符合顺序一致性模型(但可以有影响结果的重排);
  5. A happens-before B == A先于B执行,且A的操作对B可见;
  6. 变量加了volatile修饰后,保证了代码中,按顺序对变量的写happens-before对变量的读;


1、什么是重排序?

计算机在执行程序时,编译器和处理器为了提高性能对指令做出的重排

(1)为什么指令重排序可以提高性能?

指令重排可以减少计算机在流水线工作时候的中断,提高效率。

流水线(pipeline)技术:
是指在程序执行时多条指令重叠进行操作的一种准并行处理实现技术。

简单地说,每一个指令都会包含多个步骤,每个步骤可能使用不同的硬件。因此,流水线技术产生了,它的原理是指令1还没有执行完,就可以开始执行指令2,而不用等到指令1执行结束之后再执行指令2,这样就大大提高了效率。

但是,流水线技术最害怕中断,恢复中断的代价是比较大的,所以我们要想尽办法不让流水线中断。指令重排就是减少中断的一种技术。

(2)指令重排的分类:

a. 编译器优化重排
编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。

b. 指令并行重排
现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性(即后一个执行的语句无需依赖前面执行的语句的结果),处理器可以改变语句对应的机器指令的执行顺序。

c. 内存系统重排
由于处理器使用缓存和读写缓存冲区,这使得加载(load)和存储(store)操作看上去可能是在乱序执行,因为三级缓存的存在,导致内存与缓存的数据同步存在时间差。

指令重排可以保证串行语义一致,但是没有义务保证多线程间的语义也一致。所以在多线程下,指令重排序可能会导致一些问题。

2、顺序一致性模型?

(1)什么是数据竞争?

数据竞争:在一个线程中写一个变量,在另一个线程读同一个变量,并且写和读没有通过同步来排序。

  • 数据竞争可能会导致结果不确定(出错);
  • 正确同步的程序,那么就不存在数据竞争。

(2)什么是顺序一致性模型?

  • 顺序一致性模型是一个理想化的多线程模型
  • 一个线程中的所有操作按照代码的顺序来执行
  • 线程的每个操作必须是原子性的;
  • 线程的操作立刻对所有线程可见

满足以上条件的多线程模型就是“顺序一致性模型”。
顺序一致性模型保证了内存的可见性(操作对于不同线程立即可见);
顺序一致性模型中没有数据竞争(操作对于不同线程立即可见);

(3)JMM与顺序一致性模型

JMM保证:如果程序是正确同步的,程序的执行将具有顺序一致性。
即:
a. 程序正确同步,JMM保证:

  • 每个线程按顺序执行,且操作立即对任意线程可见!
  • 线程的每个操作具有原子性!
  • 临界区内程序可以重排(以优化性能),但不改变程序的执行结果!
  • 线程看到的执行顺序就是按照代码顺序的!

b. 程序未正确同步,JMM保证:

  • 线程读取到的值,要么是之前某个线程写入的值,要么是默认值,不会无中生有;
  • 单线程里面操作重排后结果不变!
  • 可能不同线程看到的执行顺序不同;
  • 对long和double变量的写操作具有不一定具有原子性!

Happens-Before

(1)什么是Happens-Before?

Happens-Before是一种关系的定义(类似因果关系的定义);

若A操作 happens-before B操作,那么

  • A操作的执行结果将 对B操作可见;
  • A操作的执行顺序 排在B操作之前!

(2)Java里面的Happens-Before关系

  • 程序顺序规则:一个线程中的每一个操作,happens-before于该线程中的任意后续操作。
  • 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
  • volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
  • 传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。
  • start规则:如果线程A执行操作ThreadB.start()启动线程B,那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作、
  • join规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。

参考资料

http://concurrent.redspider.group/article/02/7.html
《深入浅出多线程》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值