[英]Non volatile double checked locking, is it possible?
這是我的單身人士班。
靜態instance
字段不是易變的,因此出現了重新排序/可見性問題。 為了解決它,實例val
字段被設為final。 由於實例是正確構造的,因此如果它們的客戶端完全看到實例,則它們的客戶端應始終看到val
域已初始化。
static class Singleton {
private static Singleton instance;
private final String val;
public Singleton() { this.val = "foo"; }
public static Singleton getInstance() {
if (instance == null)
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
return instance;
}
public String toString() { return "Singleton: " + val; }
}
但是,還有另一個問題-我對“實例”字段進行了兩個不受保護的讀取,可以對它們進行重新排序(?),以便客戶端可以獲取null而不是實際值:
public static Singleton getInstance() {
Singleton temp = instance;
if (instance != null) return temp;
else { /* init singleton and return instance*/ }
}
要解決此問題,我覺得我可以引入局部變量:
public static Singleton getInstance() {
Singleton temp = instance;
if (temp == null)
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
temp = instance;
}
}
return temp;
}
這似乎解決了問題,因為只有一個不受保護的價值讀取,因此沒有真正的邪惡發生。 但是...我剛剛修改了程序流程,而沒有(幾乎完成?)更改其單線程語義。 這是否意味着編譯器可以撤消我的解決方法,因為這種轉換是安全的,並且如果不與volatile建立正確的事前關系,就無法使該代碼正常工作?
我不確定是否真的會發生對相同變量的讀取的重新排序,但是可以確保局部變量不受其他線程活動的影響。 即使沒有發生這種讀取重新排序,此保證也適用於在讀取時可能同時更新的每個變量:如果讀取值並將其存儲到局部變量中,則可以確保局部變量的值確實之后不會突然改變。 當然,如果值是引用,則該保證不適用於引用對象的字段。
相關句子可以在JLS§17.4.1中找到:
局部變量(第14.4節),形式方法參數(第8.4.1節)和異常處理程序參數(第14.20節)永遠不會在線程之間共享,並且不受內存模型的影響。
因此答案是否定的,不允許編譯器撤消引入局部變量的解決方法。
進行延遲初始化單例的最安全方法是使用另一個類來保存單個實例字段,並依靠Java語言為類初始化提供的保證
public class Singleton {
private static class Holder {
static final Singleton instance = new Singleton();
}
public Singleton getInstance() {
return Holder.instance;
}
}
僅在首次調用getInstance()
時初始化Holder
類(並因此創建實例)。
我認為您從一開始就沒有問題。
您使用synchronized(Singleton.class)
。 synchronized
Java會保證在此關鍵字容易反映到所涉及變量的內存之前進行任何讀/寫操作。 由於您的Singleton instance
也是在類級別聲明的,因此對其的任何修改都易於從其他類中看到,並填充到主內存中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.