您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關JMM指令重排序的示例分析的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
在計算機系統中,指令為了更快的完成結果,會根據邏輯關系、指令大小進行重排序,以達到超流水線的效果,但在代碼執行完后,保證結果輸出是一致的。
在JVM中又兩條原則:
as-if -serials 這個規則適用于單線程,默認自動處理
happens-before 這個規則適用于多線程,當然也要通過相應的鎖和關鍵字來實現
我們知道,JIT會根據CPU架構編譯合適的代碼去執行,因此,在匯編層面討論反而不見得能統一意見,比如X86,他只有lock來實現,其他CPU架構則是LoadLoad
,StoreStore
,LoadStore
,StoreLoad
。
實際上我們還是在JIT層面思考比較容易理解,這里我們定義一個類
(注意:這個類未必和JIT實際效果一致,取決于JIT激進程度)
package com.apptest; public class VolatileWatcher { private String A; private String B; private String C; private String D; private String E; public void run() { String a = "a"; String b = "b"; String c = "c"; String d = "d"; String e = "e"; A = a; B = b; C = c; D = d; E = e; System.out.println(A); System.out.println(B); System.out.println(C); System.out.println(D); System.out.println(E); } }
那么如果重排序,效果可能如下
package com.apptest; public class VolatileWatcher { private String A; private String B; private String C; private String D; private String E; public void run() { String a = "a"; String b = "b"; String c = "c"; String d = "d"; String e = "e"; A = a; System.out.println(A); B = b; System.out.println(B); C = c; System.out.println(C); D = d; System.out.println(D); E = e; System.out.println(E); } }
這是JIT可能重排序后的結果,實際上寄存器、CPU也會進行重排序,但最終也會保證內存一致性。
Java中,禁止指令重排序的關鍵字只有2個,一個是volatile、另一個是final,某種成都上,也能說明final和volatile不能同時修飾一個變量的原因,因為他們的功能有些重疊。
3 .1 要不要禁止
首先要確定要不要禁止重排序,重排序往往在多線程中出現問題,如果程序在串行執行,完全沒有必要,因此,單線程中的引用類型(不包括常量和字符串常量,這些類型引用建議加final)建議不要加final和volatile,當然,final需要考慮深入一些,因為有時我們需要做一些強約束,但總體來說能不加就不加。
3.2 原理
多線程中,維護hanpens-before原則不是JVM自身就能處理的,還需要在代碼層面進行相應的指示,此外,相應的JIT會將指示編譯成指令,讓寄存器和CPU也遵守。
指令重排序的達到的最終效果
讀操作
寫操作
3.3 效果演示
回到文章開頭,我們給VolatileWatcher的C變量添加volatile修飾,那么JIT重排序后的結果可能是如下情況
public class VolatileWatcher { private String A; private String B; private volatile String C; private String D; private String E; public void run() { String a = "a"; String b = "b"; String c = "c"; String d = "d"; String e = "e"; B = b; A = a; C = c; //寫屏障 E = e; D = d; System.out.println(A); System.out.println(B); System.out.println(C); //讀屏障 System.out.println(D); System.out.println(E); } }
我們發現,A、B不能跨越C變量的寫屏障往下重排序,但是屏障上方的A、B之間也是可以重排序的,E、D不能跨越讀屏障,但是E、D可以在讀屏障和寫屏障之間排序。(這里故意寫成A,B,E,D在屏障外順序變化,目的主要是為了說明屏障的作用,具體看實際效果)
我們知道,final修飾的是只讀變量,有很強的“只讀”約束性,所有final修飾的變量都需要在構造方法中初始化(類外部final字段的最終會插入到super(...)之后 ),而volatile一般用于多線程變量的讀寫,但他們都具備實現內存屏障的能力,可以說,都能實現禁止重排序。
不同點:
final字段的寫操作,只作用于構造函數。
final字段的讀操作,讀取和volatile基本一樣
感謝各位的閱讀!關于“JMM指令重排序的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。