电竞比分网-中国电竞赛事及体育赛事平台

分享

Java內(nèi)存模型中的同步和易失性如何工作?

 印度阿三17 2019-06-27

在“Effective Java”一書中:

// Broken! - How long would you expect this program to run?
public class StopThread {

    private static boolean stopRequested;

    public static void main(String[] args) throws InterruptedException {
        Thread backgroundThread = new Thread(new Runnable() {
            public void run() {
                int i = 0;
                while (!stopRequested)
                    i  ;
            }
        });
        backgroundThread.start();

        TimeUnit.SECONDS.sleep(1);
        stopRequested = true;
    }
}

backgroundThread在一秒鐘后不會停止.因為吊裝,在JVM,HotSpot服務(wù)器VM中進(jìn)行優(yōu)化.

您可以在以下主題中查看此內(nèi)容:
Why HotSpot will optimize the following using hoisting?.

優(yōu)化如下:

if (!done)
    while (true)
        i  ;

有兩種方法可以解決問題.

1.使用揮發(fā)性

private static volatile boolean stopRequested;

volatile的功能是
– 禁止吊裝
– 它保證讀取該字段的任何線程都將看到最近寫入的值

2.使用同步

public class StopThread {

    private static boolean stopRequested;

    private static synchronized void requestStop() {
        stopRequested = true;
    }

    private static synchronized boolean stopRequested() {
        return stopRequested;
    }

    public static void main(String[] args)
                throws InterruptedException {
        Thread backgroundThread = new Thread(new Runnable() {
            public void run() {
                int i = 0;
                while (!stopRequested())
                    i  ;
            }
        });
        backgroundThread.start();

        TimeUnit.SECONDS.sleep(1);
        requestStop();
    }
}

上面的代碼正好在Effective Java書中,它相當(dāng)于使用volatile來裝飾stopRequested.

private static boolean stopRequested() {
    return stopRequested;
}

如果此方法省略synchronized關(guān)鍵字,則此程序運行不正常.
我認(rèn)為當(dāng)方法省略synchronized關(guān)鍵字時,此更改會導(dǎo)致提升.
是對的嗎?

解決方法:

要清楚地理解為什么會發(fā)生這種情況,您需要了解更深層次發(fā)生的事情. (這基本上是對所謂之前發(fā)生的關(guān)系的解釋,我希望這種語言對于讀者更為明白).

通常,變量存在于RAM存儲器中.當(dāng)一個線程需要使用它們時,它會從RAM中取出它們并將它們放入緩存中,以便它可以盡快訪問它們直到需要它們?yōu)橹?

使用volatile強制一個線程直接從RAM內(nèi)存中讀取和寫入變量.因此,當(dāng)許多線程使用相同的volatile變量時,它們都會看到RAM內(nèi)存中存在的最后一個版本,而不是緩存中可能的舊副本.

當(dāng)線程進(jìn)入同步塊時,它需要控制監(jiān)視器變量.所有其他線程一直等到第一個線程從synchronized塊退出.為確保所有線程都能看到相同的修改,同步塊中使用的所有變量都直接從RAM內(nèi)存中讀取和寫入,而不是從高速緩存副本中讀取.

因此,如果您嘗試在沒有synchronized方法或沒有volatile關(guān)鍵字的情況下讀取變量stopRequested,則可以讀取緩存中存在的可能的舊副本.

要解決這個問題,您需要確保:

>所有線程都使用volatile變量
>或訪問這些變量的所有線程都使用同步塊.

使用方法

private static boolean stopRequested() {
   return stopRequested;
}

如果沒有synchronized關(guān)鍵字,并且stopRequested不是volatile,則意味著您可以從無效的緩存副本中讀取stopRequested的值.

來源:https://www./content-3-274801.html

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多