淺析_單例模式
阿新 • • 發佈:2020-08-16
單例模式
單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種型別的設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。
這種模式涉及到一個單一的類,該類負責建立自己的物件,同時確保只有單個物件被建立。這個類提供了一種訪問其唯一的物件的方式,可以直接訪問,不需要例項化該類的物件。
注意:
- 單例類只能有一個例項。
- 單例類必須自己建立自己的唯一例項。
- 單例類必須給所有其他物件提供這一例項。
1、餓漢式
特點:類一載入,就初始化了!因為類只會載入一次,故不存線上程安全問題。
public class Hungry { private static final Hungry HUNGRY = new Hungry(); private Hungry(){} public static Hungry getInstance(){ return HUNGRY; } }
2、懶漢式(DCL)
特點:延遲載入,當需要呼叫的時候,再初始化!故多執行緒下存線上程安全問題。
這個比較經典,所以談談其中的細節:
(1)為什麼雙重校驗?
第一重校驗,是防止沒有必要的加鎖,避免影響 get 例項的效率;
第二重校驗,是防止加鎖的過程中,執行緒A突然阻塞,其他的執行緒搶先例項化了,而後面執行緒A被喚醒,導致物件被二次例項化。
(2)為什麼要加 volatile 關鍵字?
因為物件的初始化,大致可以分為三步:
- 分配記憶體空間
- 執行構造方法,例項化物件
- 把棧幀裡定義的物件指向記憶體空間(棧中存放的是引用地址,只要指向了,就不為null 了)
那麼問題就來了:如果指令重排了,第二步初始化最後執行,然後陷入阻塞。那麼會有可能會導致,有執行緒獲取的單例物件,其實沒有初始化!
public class Lazy { private volatile static Lazy lazy; private Lazy(){ } // 雙重檢測鎖,懶漢式單例,DCL懶漢式 public static Lazy getInstance(){ if(lazy == null){ // 防止非必要加鎖 synchronized (Lazy.class){ if(lazy == null){ // 防止有執行緒在加鎖的過程中,對單例物件初始化了 lazy = new Lazy(); // 不是原子性操作 } } } return lazy; } }
3、靜態內部類
特點:和DCL懶漢式類似,但缺點是 外部無法傳遞引數 ,具體使用需自行取捨!執行緒安全是因為它也是利用了類載入的特性。
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
// 延遲載入
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
4、列舉類
特點:最安全的單例,且 不會被反射破壞單例模式!(因為反射原始碼裡有對物件型別進行判斷,是否為列舉類)
package com.feng.single;
import java.lang.reflect.Constructor;
/**
* enum 本質也是一個Class 類
* 列舉類實現單例:
* 最安全,反射也沒有辦法破解它的單例。
*/
public enum EnumSingle {
MY_INSTANCE;
public EnumSingle getInstance(){
return MY_INSTANCE;
}
}
觀看原始碼得知:列舉類才是最安全的單例模式。因為連反射都沒辦法去構造它的物件,破壞它的單例!
5、總結
以上就是單例的幾種實現方式了,就各自的特點來說,DCL懶漢式 和 列舉 是比較適合使用的。