|
在“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
|