java_亂序執行


什么是亂序執行

  CPU運行的時候,是按照指令一條一條執行的。CPU速度特別快,但是CPU從內存去取數據的話,會很慢。這時候,就可能出現后來的指令要比先到的指令先執行的情況,例如:現在給CPU兩條指令 ,兩個指令沒有關系。第一條指令從內存讀數據。需要等待很長時間,那么在等待內存的過程中,會先執行指令2。然后等數據到了以后,然后執行指令1。看起來就是指令2 比指令1先執行。這些亂序執行,是CPU自動優化的結果,可以提高效率。而且如果第二條指令依賴第一條指令,不會發生指令亂序執行。

  亂序執行的測試代碼:

/**
 * 指令重排序驗證,在這個案例代碼中,可能出現場景如下:
 * 1.one線程執行完畢后執行other 線程結果:a=1,x=0;b=1,y=1; xy值為:01組合
 * 2.other線程執行完畢后執行one 線程結果:a=1,x=1;b=1,y=0; xy值為:10組合
 * 3.other線程和one線程同時執行 線程結果:a=1,x=1;b=1,y=1; xy值為:11組合
 * 按照正常邏輯,不可能出現 xy值為00的組合
 * 
 * 
 * 出現這種情況,只有一種可能,出現亂序執行了。
 * @author LYs
 *
 */
public class T04_Disorder {

    private static int x = 0,y=0;
    private static int a= 0,b=0;
    
    public static void main(String[] args) throws Exception {
        int i=0;
        for (; ;) {
            i++;
            x=0;y=0;
            a=0;b=0;
            Thread one = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    a=1;
                    x=b;
                }
            });
            Thread other = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    b=1;
                    y=a;
                }
            });
            
            one.start();other.start();
            one.join();other.join();
            String result = "第"+i+"次("+x+","+y+")";
            if (x==0 && y==0) {
                System.err.println(result);
                break;
            }
        }
    }
}

亂序執行有可能帶來的問題

  有時候亂序執行會導致一些問題,單線程情況下,好像基本上沒有啥影響。但是如果是多線程的話,就有可能會導致一些問題出現。舉例:我們在創建對象的時候, new方法會產生4條指令。舉例說明:

java代碼

public  class Test {
    int i = 8;
}

指令如下:

1  NEW Test2  DUP
3  INVOKESPECIAL Test.<init>()V
4  ASTORE 1

  其中第一行是在內存中為對象分配一塊空間,這時候int i 的值是默認值0 。第二行是將指針緩存下這個不需要理解不影響分析亂序執行問題。第三行指令執行操作,將8賦值給變量i 。第四行指令將該內存的對象引用賦值給棧內的引用。當有兩個線程,線程1創建test對象,同時線程2訪問該對象,如果不為空,就取出i的值去進行操作。這時如果指令第三行和第四行發生重排序就會出現問題:

 

   圖片來源於馬士兵線程課

  如圖所示,在線程1創建對象並給i默認值為0的時候,將線程的對象引用賦值給了棧中的對象變量。這時,在對該對象做非空判斷時得到的結果是該對象存在。線程2就會去取線程1new出來的對象,但是線程1並沒有執行指令4 init的方法。線程2 取到的值為0 但是正常操作應該取到的值為8 這就是指令重排序帶來的問題。

指令重排序問題解決

  指令重排序問題的解決辦法是內存屏障,實現方式為:在執行的時候,加一堵牆,牆兩邊的指令不允許相互之間發生重排序。內存屏障的實現是在CPU級別實現的。Intel硬件提供了一系列的內存屏障,主要有: 
1. lfence,是一種Load Barrier 讀屏障 
2. sfence, 是一種Store Barrier 寫屏障 
3. mfence, 是一種全能型的屏障,具備ifence和sfence的能力 
4. Lock前綴,Lock不是一種內存屏障,但是它能完成類似內存屏障的功能。Lock會對CPU總線和高速緩存加鎖,可以理解為CPU指令級的一種鎖。

java代碼想要實現內存屏障,只需要在相應的變量前添加volatile關鍵字。這個關鍵字的作用為:1.保證可見性,2.防止指令重排序。jvm實現防止內存重排序使用的辦法是鎖定cpu總線,該辦法效率較低,但是兼容性比較好。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM