單例主要有兩種加載思想:
1.強調響應速度和反應時間 - 非延遲加載,又稱餓漢式
2.強調資源利用效率 - 延遲加載,又稱懶漢式
非延遲加載單例類 餓漢式
public class Singleton {
private Singleton() {}
private static final Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
同步延遲加載 懶漢式
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
雙重檢測同步延遲加載
為避免非延遲加載瓶頸,需要對 instance進行第二次檢查。因為這里的同步只需在第一次創建實例時才同步,創建成功以后獲取實例時不再需要獲取同步鎖。為避免JIT編譯器對代碼的指令重排序,使用volatile關鍵字。
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
使用ThreadLocal修復雙重檢測
借助于ThreadLocal
,將臨界資源(需要同步的資源)線程局部化。具體到就是將雙重檢測的第一層檢測條件 if (instance == null)
轉換為了線程局部范圍內來操作。
這里的ThreadLocal也只是用作標示而已,用來表示每個線程是否已訪問過。如果訪問過,則不再需要走同步塊,這樣就提高了效率。
但是ThreadLocal
在Java1.4
以前的版本都較慢,但這與volatile
相比卻是安全的。
public class Singleton {
private static final ThreadLocal perThreadInstance= new ThreadLocal();
private static Singleton singleton ;
private Singleton() {}
public static Singleton getInstance() {
if (perThreadInstance.get() == null){
// 每個線程第一次都會調用
createInstance();
}
return singleton;
}
private static final void createInstance() {
synchronized (Singleton.class) {
if (singleton == null)
singleton = new Singleton();
}
perThreadInstance.set(perThreadInstance);
}
}
延遲加載
類的延遲加載:
public class Singleton {
private Singleton() {}
public static class Holder {
static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return Holder.instance;
}
}