[英]Java, will volatile also guarantee visibility?
public class Stuff {
private final Timer timer = new Timer(true);
public static final int DEFAULT_TIMEOUT = 1500;
private volatile int timeout = DEFAULT_TIMEOUT;
public void doStuff(OtherStuff) {
...
timer.schedule(timeout, ...);
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public int getTimeout() {
return timeout;
}
}
除了可以從另一個類更改的超時變量外,只從1個線程訪問此類的實例。 在我的例子中是一個JMX bean,這意味着可以在運行時從管理界面改變超時。
doStuff()可以運行100次/秒,而setTimeout()可以每周運行一次 - 因此執行setTimeout()的人和執行doWork()的人之間的順序並不重要。
對於這種情況, timeout
是否足夠? 內存模型是否可以保證將一個線程設置為doStuff()
方法可見?
另一種似乎安全的替代方案就是:
public class Stuff {
private final Timer timer = new Timer(true);
public static final int DEFAULT_TIMEOUT = 1500;
private int timeout = DEFAULT_TIMEOUT;
public void doStuff(OtherStuff) {
...
timer.schedule(getTimeout(), ...);
}
public void synchronized setTimeout(int timeout) {
this.timeout = timeout;
}
public int synchronized getTimeout() {
return timeout;
}
}
哪兩種方法更受青睞?
從可見性的角度來看,兩種方法都是等價的。 在對同一個volatile變量進行寫操作之后發生的對volatile的任何讀取都可以保證看到寫入。
所以如果一個線程寫入timeout = newValue;
,隨后調用timer.schedule(timeout)
任何其他線程都可以保證看到newValue
。
此保證在JLS 17.4.5中指定:
寫入易失性字段(第8.3.1.4節) - 在每次后續讀取該字段之前發生。
我會簡單地使用volatile作為它提供的保證就足夠了,它清楚地顯示了你的意圖。
盡管這個例子的方法都是等價的,但我認為(由於問題的普遍性),值得一提的是:
volatile變量的可見性效果超出了volatile變量本身的值。 如果問題是“將揮發性還能保證同一個易變量的可見性嗎?”。 答案是肯定的。
但是,在寫入此變量之前,無法保證(如果有)其他變量的可見性。
從完全不穩定的可見性保證 。
例:
當線程A向其寫入的易失性可變,並隨后線程B讀取同一變量, 所有的變量,要寫入到易失性可變成為乙可見之后讀取所述易失性可變是可見的到先前的值。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.