1. 常見方式
保證線程安全條件下有三種方式:
提前初始化:類加載時就創建一個對象,
延遲初始化
DCL:雙重檢查,JDK5.0後單例實例加volatile修飾
佔位類:推遲佔位類的初始化
提前初始化可能影響程序啓動時間以及不必要高開銷操作較少採用
2. DCL
public class DoubleCheckLocking { private static Resource resource; public static Resource getInstance(){ if(resource == null){ synchronized (DoubleCheckLocking.class){ if (resource == null){ resource = new Resource() } } } return resource } }
上述代碼的問題在於在沒有同步的情況下讀取一個共享對象,可能發生的情況是獲取一個沒有完成初始化的對象,部分屬性是失效的。
在缺少Happens-Before關係時,可能出現指令重排序。初始化對象如果需要寫入多個變量,發佈對象引用也需要寫入一個變量。上述無法保證發佈引用在另一個線程加載該引用前,新對象引用的寫操作與屬性的寫操作會重排,這樣新線程會看到引用的最新值,但是部分屬性失效。
JDK5.0後爲resource家volatile修飾可以使用DCL,但是這種方法已經被廣泛廢棄,佔位模式更容易理解
3. 佔位類
public class DoubleCheckLocking { private static class ResourceHolder{ public static Resource resource = new Resource(); } public static Resource getInstance(){ return ResourceHolder.resource; } }