溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何實(shí)現(xiàn)指令重排序

發(fā)布時間:2021-10-21 17:19:34 來源:億速云 閱讀:159 作者:iii 欄目:編程語言

本篇內(nèi)容主要講解“如何實(shí)現(xiàn)指令重排序”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“如何實(shí)現(xiàn)指令重排序”吧!

(一)什么是指令重排序

為了使處理器內(nèi)部的運(yùn)算單元能盡量被充分利用,處理器可能會對輸入的代碼進(jìn)行亂序執(zhí)行優(yōu)化,處理器會在計算之后將亂序執(zhí)行的結(jié)果重組,并確保這一結(jié)果和順序執(zhí)行結(jié)果是一致的,但是這個過程并不保證各個語句計算的先后順序和輸入代碼中的順序一致。這就是指令重排序。

簡單來說,就是指你在程序中寫的代碼,在執(zhí)行時并不一定按照寫的順序。

在Java中,JVM能夠根據(jù)處理器特性(CPU多級緩存系統(tǒng)、多核處理器等)適當(dāng)對機(jī)器指令進(jìn)行重排序,最大限度發(fā)揮機(jī)器性能。

Java中的指令重排序有兩次,第一次發(fā)生在將字節(jié)碼編譯成機(jī)器碼的階段,第二次發(fā)生在CPU執(zhí)行的時候,也會適當(dāng)對指令進(jìn)行重排。

(二)復(fù)現(xiàn)指令重排序

光靠說不容易看出現(xiàn)象,下面來看一段代碼,這段代碼網(wǎng)上出現(xiàn)好多次了,但確實(shí)很能復(fù)現(xiàn)出指令重排序。我把解釋放在代碼后面。

public class VolatileReOrderSample {
    //定義四個靜態(tài)變量
    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;
            //開兩個線程,第一個線程執(zhí)行a=1;x=b;第二個線程執(zhí)行b=1;y=a
            Thread thread1=new Thread(new Runnable() {
                @Override
                public void run() {
                    //線程1會比線程2先執(zhí)行,因此用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();
            //等兩個線程都執(zhí)行完畢后拼接結(jié)果
            String result="第"+i+"次執(zhí)行x="+x+"y="+y;
            //如果x=0且y=0,則跳出循環(huán)
            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);
    }
}

這段代碼雖然看著長,其實(shí)很簡單,定義四個靜態(tài)變量x,y,a,b,每次循環(huán)時讓他們都等于0,接著用兩個線程,第一個線程執(zhí)行a=1;x=b;第二個線程執(zhí)行b=1;y=a。

這段程序有幾個結(jié)果呢?從邏輯上來講,應(yīng)該有3個結(jié)果:

當(dāng)?shù)谝粋€線程執(zhí)行到a=1的時候,第二個線程執(zhí)行到了b=1,最后x=1,y=1

當(dāng)?shù)谝粋€線程執(zhí)行完,第二個線程才剛開始,最后x=0,y=1

當(dāng)?shù)诙€線程執(zhí)行完,第一個線程才開始,最后x=1,y=0

理論上無論怎么樣都不可能x=0,y=0;

但是當(dāng)程序執(zhí)行到幾萬次之后,竟然出現(xiàn)了00的結(jié)果:

如何實(shí)現(xiàn)指令重排序

這就是因?yàn)橹噶畋恢嘏判蛄耍瑇=b先于a=1執(zhí)行,y=a先于b=1執(zhí)行。

如何實(shí)現(xiàn)指令重排序

(三)通過什么方式禁止指令重排序?

Volatile通過內(nèi)存屏障可以禁止指令重排序,內(nèi)存屏障是一個CPU的指令,它可以保證特定操作的執(zhí)行順序。

內(nèi)存屏障分為四種:

StoreStore屏障、StoreLoad屏障、LoadLoad屏障、LoadStore屏障。

JMM針對編譯器制定了Volatile重排序的規(guī)則:

如何實(shí)現(xiàn)指令重排序

光看這些理論可能不容易懂,下面我就用通俗的話語來解釋一下:

首先是對四種內(nèi)存屏障的理解,Store相當(dāng)于是寫屏障,Load相當(dāng)于是讀屏障。

比如有兩行代碼,a=1;x=2;并且我把x修飾為volatile。

執(zhí)行a=1時,它相當(dāng)于執(zhí)行了一次普通的寫操作;

執(zhí)行x=2時,它相當(dāng)于執(zhí)行了一次volatile的寫操作;

因此在這兩行命令之間,就會插入一個StoreStore屏障(前面是寫后面也是寫),這就是內(nèi)存屏障。

再讓我們看表,如果第一個操作是普通寫,第二個操作是volatile寫,那么表格中對應(yīng)的值就是NO,禁止重排序。這就是Volatile進(jìn)行指令重排序的原理。

現(xiàn)在,我們只需要把上面代碼的x和y用volatile修飾,就不會發(fā)生指令重排序了(如果你能通過表推一遍邏輯,你就能懂了)。

到此,相信大家對“如何實(shí)現(xiàn)指令重排序”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI