您好,登錄后才能下訂單哦!
本篇內容主要講解“如何實現指令重排序”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“如何實現指令重排序”吧!
為了使處理器內部的運算單元能盡量被充分利用,處理器可能會對輸入的代碼進行亂序執行優化,處理器會在計算之后將亂序執行的結果重組,并確保這一結果和順序執行結果是一致的,但是這個過程并不保證各個語句計算的先后順序和輸入代碼中的順序一致。這就是指令重排序。
簡單來說,就是指你在程序中寫的代碼,在執行時并不一定按照寫的順序。
在Java中,JVM能夠根據處理器特性(CPU多級緩存系統、多核處理器等)適當對機器指令進行重排序,最大限度發揮機器性能。
Java中的指令重排序有兩次,第一次發生在將字節碼編譯成機器碼的階段,第二次發生在CPU執行的時候,也會適當對指令進行重排。
光靠說不容易看出現象,下面來看一段代碼,這段代碼網上出現好多次了,但確實很能復現出指令重排序。我把解釋放在代碼后面。
public class VolatileReOrderSample { //定義四個靜態變量 private static int x=0,y=0; private static int a=0,b=0; public static void main(String[] args) throws InterruptedException { int i=0; while (true){ i++; x=0;y=0;a=0;b=0; //開兩個線程,第一個線程執行a=1;x=b;第二個線程執行b=1;y=a Thread thread1=new Thread(new Runnable() { @Override public void run() { //線程1會比線程2先執行,因此用nanoTime讓線程1等待線程2 0.01毫秒 shortWait(10000); a=1; x=b; } }); Thread thread2=new Thread(new Runnable() { @Override public void run() { b=1; y=a; } }); thread1.start(); thread2.start(); thread1.join(); thread2.join(); //等兩個線程都執行完畢后拼接結果 String result="第"+i+"次執行x="+x+"y="+y; //如果x=0且y=0,則跳出循環 if (x==0&&y==0){ System.out.println(result); break; }else{ System.out.println(result); } } } //等待interval納秒 private static void shortWait(long interval) { long start=System.nanoTime(); long end; do { end=System.nanoTime(); }while (start+interval>=end); } }
這段代碼雖然看著長,其實很簡單,定義四個靜態變量x,y,a,b,每次循環時讓他們都等于0,接著用兩個線程,第一個線程執行a=1;x=b;第二個線程執行b=1;y=a。
這段程序有幾個結果呢?從邏輯上來講,應該有3個結果:
當第一個線程執行到a=1的時候,第二個線程執行到了b=1,最后x=1,y=1;
當第一個線程執行完,第二個線程才剛開始,最后x=0,y=1;
當第二個線程執行完,第一個線程才開始,最后x=1,y=0;
理論上無論怎么樣都不可能x=0,y=0;
但是當程序執行到幾萬次之后,竟然出現了00的結果:
這就是因為指令被重排序了,x=b先于a=1執行,y=a先于b=1執行。
Volatile通過內存屏障可以禁止指令重排序,內存屏障是一個CPU的指令,它可以保證特定操作的執行順序。
內存屏障分為四種:
StoreStore屏障、StoreLoad屏障、LoadLoad屏障、LoadStore屏障。
JMM針對編譯器制定了Volatile重排序的規則:
光看這些理論可能不容易懂,下面我就用通俗的話語來解釋一下:
首先是對四種內存屏障的理解,Store相當于是寫屏障,Load相當于是讀屏障。
比如有兩行代碼,a=1;x=2;并且我把x修飾為volatile。
執行a=1時,它相當于執行了一次普通的寫操作;
執行x=2時,它相當于執行了一次volatile的寫操作;
因此在這兩行命令之間,就會插入一個StoreStore屏障(前面是寫后面也是寫),這就是內存屏障。
再讓我們看表,如果第一個操作是普通寫,第二個操作是volatile寫,那么表格中對應的值就是NO,禁止重排序。這就是Volatile進行指令重排序的原理。
現在,我們只需要把上面代碼的x和y用volatile修飾,就不會發生指令重排序了(如果你能通過表推一遍邏輯,你就能懂了)。
到此,相信大家對“如何實現指令重排序”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。