聊聊什麼是指令重排,如何防止指令重排

什麼是指令重排

java語言規範規定JVM執行緒內部維持順序化語義。即只要程式的最終結果與它順序化情況的結果相等,那麼指令的

執行順序可以與程式碼順序不一致,此過程叫指令的重排序。

圖例:

聊聊什麼是指令重排,如何防止指令重排

發生時間:編譯期、cpu指令重排

cpu指令最佳化原則

as-if-serial語義:

不管怎麼重排序,程式的執行結果不能被改變

happens-before 原則:

1. 程式順序原則:

即在一個執行緒內必須保證語義序列性,也就是說按照程式碼順序執行。

2. 鎖規則:

解鎖(unlock)操作必然發生在後續的同一個鎖的加鎖(lock)之前,也就是說,如果對於一個鎖解鎖後,再加鎖,那麼加鎖的動作必須在解鎖動作之後(同一個鎖)。

3. volatile規則:

volatile變數的寫,先發生於讀,這保證了volatile變數的可見性,簡單的理解就是,volatile變數在每次被執行緒訪問時,都強迫從主記憶體中讀該變數的值,而當該變數發生變化時,又會強迫將最新的值重新整理到主記憶體,任何時刻,不同的執行緒總是能

夠看到該變數的最新值。

4. 執行緒啟動規則:

執行緒的start()方法先於它的每一個動作,即如果執行緒A在執行執行緒B的start方法之前修改了共享變數的值,那麼當執行緒B執行start方法時,執行緒A對共享變數的修改對執行緒B可見

5. 傳遞性:

A先於B ,B先於C 那麼A必然先於C

6. 執行緒終止規則:

執行緒的所有操作先於執行緒的終結,Thread。join()方法的作用是等待當前執行的執行緒終止。假設線上程B終止之前,修改了共享變數,執行緒A從執行緒B的join方法成功返回後,執行緒B對共享變數的修改將對執行緒A可見。

7. 執行緒中斷規則:

對執行緒 interrupt()方法的呼叫先行發生於被中斷執行緒的程式碼檢測到中斷事件的發生,可以透過Thread。interrupted()方法檢測執行緒是否中斷。

8. 物件終結規則:

物件的建構函式執行,結束先於finalize()方法

使用記憶體屏障防止指令重排:

記憶體屏障:

又稱記憶體柵欄,是一個CPU指令,它的作用有兩個,一是保證特定操作的執行順序,二是保證某些變數的記憶體可見性

1.使用volatile的記憶體可見性的特性來做記憶體屏障

JMM針對編譯器制定的volatile重排序規則表:

聊聊什麼是指令重排,如何防止指令重排

2.unsafe手動加記憶體屏障:

public native void loadFence();public native void storeFence();public native void fullFence();