Java重排序和happens-before原则

    xiaoxiao2021-03-25  88

    重排序

    重排序是编译器和处理器为了优化性能而对指令执行的顺序进行重排序。大多数现代处理器都会采用将指令乱序执行的方法,在条件允许的情况下,直接运行当前有能力立即执行的后续指令,避开获取下一条指令所需数据时造成的等待。通过乱序执行的技术,处理器可以大大提高执行效率。

    也就是说程序的执行,并不是严格按照程序语句编写的顺序执行,在运行期间可能是被打乱的。

    重排序发生位置

    编译器重排序指令级并行重排序内存系统重排序

    as-if-serial

    程序在执行的时候发成重排序,那Java是如何保证程序正常执行的呢? 原因就是Java遵循as-if-serial语义,即单线程执行程序时,即使发生重排序,程序的执行结果不能被改变。 as-if-serial保证了Java程序在单线程运行的情况下,结果的正常,让我们看起来像是顺序执行的样子。

    happens-before

    为了使Java程序在各个平台执行正常,Java内存模型中规定了happens-before规则。happens-before的前后两个操作不会被重排序且后者对前者内存可见。

    happens-before规则

    程序次序规则: 线程中每个动作A都happens-before于该线程中的每一个动作B。那么在程序中,所有的动作B都能出现在A之后。监视器锁法则: 对一个监视器的解锁happens-before于每个后续对同一监视器锁的加锁volatile变量法则:对volatile域的写入操作happens-before于每一个后续对同一个域的读写操作线程启动法则: 在一个线程中,对于Thread.start的调用会happens-before于每个启动线程的动作。线程终结法则: 线程中的任何动作都happens-before于其他线程检测到这个线程已经终结。中断法则: 一个线程调用另一个线程的interrupt happens-before于被中断的线程发现中断。终结法则: 一个对象的构造函数的结束happens-before于这个对象finalizer的开始。传递性: 如果A happens-before于B,且B happens-before于C,则A happens-before于C

    volatile

    volatile有两个语义: 1. 多线程对于域的可见性 2. 禁止指令重排序

    第二个禁止对volatile的指令重排序在上面已经说明,现在说明JVM使如何保证volatile的可见性。

    Java在处理volatile读写操作时会加入相应的内存屏障,来保证一个读写的循序。

    内存屏障

    内存屏障(Memory Barrier,或有时叫做内存栅栏,Memory Fence)是一种CPU指令,用于控制特定条件下的重排序和内存可见性问题。Java编译器也会根据内存屏障的规则禁止重排序。

    LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。 StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。 LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。 StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。

    转载请注明原文地址: https://ju.6miu.com/read-26213.html

    最新回复(0)