1. 餓漢式(線程安全)
public static class Singleton{
private final static Singleton INSTANCE = new Singleton();
private Singleton{
}
public static Singleton getInstance(){
return INSTANCE;
}
}
餓漢模式在類被初始化時就已經在內存中創建了對象,以空間換時間,故不存在線程安全問題。
2. 懶漢式(線程不安全)
public static class Singleton{
private static Singleton INSTANCE = null;
private Singleton{
}
public static Singleton getInstance(){
if(INSTANCE == null){
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
懶漢模式在方法被調用後才創建對象,以時間換空間,在多線程環境下存在風險。
3. 懶漢式(線程安全,多線程下效率不高)
public static class Singleton{
private static Singleton INSTANCE = null;
private Singleton{
}
public static synchronized Singleton getInstance(){
if(INSTANCE == null){
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
4. 懶漢式(線程安全)
public static class Singleton{
private static Singleton INSTANCE = null;
static{
INSTANCE = new Singleton();
}
private Singleton{
}
public static Singleton getInstance(){
return INSTANCE;
}
}
5. 雙重鎖懶漢式(線程安全)
public static class Singleton{
private volatile static Singleton INSTANCE = null;
private Singleton{
}
public static Singleton getInstance(){
if(INSTANCE == null){
synchnronized(Singleton.class){
if(INSTANCE == null){
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
DCL模式的優點就是,只有在對象需要被使用時才創建,第一次判斷 INSTANCE == null爲了避免非必要加鎖,當第一次加載時纔對實例進行加鎖再實例化。這樣既可以節約內存空間,又可以保證線程安全。但是,由於jvm存在亂序執行功能,DCL也會出現線程不安全的情況。具體分析如下:
INSTANCE = new SingleTon();
這個步驟,其實在jvm裏面的執行分爲三步:
1.在堆內存開闢內存空間。
2.在堆內存中實例化SingleTon裏面的各個參數。
3.把對象指向堆內存空間。
由於jvm存在亂序執行功能,所以可能在2還沒執行時就先執行了3,如果此時再被切換到該線程上,由於執行了3,INSTANCE 已經非空了,會被直接拿出來用,這樣的話,就會出現異常。這個就是著名的DCL失效問題。
不過在JDK1.5之後,官方也發現了這個問題,故而具體化了volatile,即在JDK1.6及以後,只要定義爲private volatile static SingleTon INSTANCE = null;就可解決DCL失效問題。volatile確保INSTANCE每次均在主內存中讀取,這樣雖然會犧牲一點效率,但也無傷大雅。
6. 靜態內部類(線程安全,推薦)
public static class Singleton{
private final static class SingletonHolder{
private final static Singleton INSTANCE = new Singleton();
}
private Singleton{
}
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}