單例模式
1.什么是單例模式?
單例模式的目的是保證一個類里只有一個實體,并提供一個訪問它的全域訪問點,
2.單例模式的設計方法:
2.1懶漢式:
- 一個私有的建構式:確保只能由類自身創建實體,不能被外部構造或被子類繼承,
- 靜態的未實體化的私有成員變數:即該類的實體,確保一個類只能有一個實體,
- 靜態的公用工廠方法:全域訪問點提供給其他類呼叫獲取實體,
當要使用這個類時,通過該類的工廠方法生成該類的實體,工廠方法會判斷實體是否已經存在,存在則回傳該實體,否則創建一個實體回傳,
public class SingleTon2(){
private SingleTon2(){
}
private static SingleTon2 singleton2 = null;
public static getInstance(){
if(singleton2 == null){
singleton2 = new SingleTon2();
}
return singleton2 ;
}
}
2.2餓漢式:
- 實體化的靜態私有成員變數:確保實體不能直接被外界獲取,
- 私有的空參構造器:防止系統默認生成公有的空參構造器,
- 靜態的共有方法:全域訪問點提供給其他類呼叫獲取實體,
public class SingleTon1(){
private SingleTon1(){
}
private static SingleTon1 singleton = new SingleTon1();
public static getInstance(){
return singleton ;
}
}
3.兩種設計方法對比
3.1普通的懶漢式單例模式
優點
-
使用物件延遲加載的思想:效率高,
?延遲加載:程式啟動時并不立即加載資源或者資料,等到馬上要使用時再加載,
缺點
-
執行緒不安全的,
普通的懶漢式單例模式不具有原子性,當程式中參考多執行緒時,系統中可能會出現多個單例類的實體物件,這違背了單例模式的初衷,
-
操作比餓漢式復雜
優化
1. 在工廠方法前加上synchronized關鍵字確保原子性
//除第一次使用,getInstance()需要同步,后面getInstance()不需要同步;每次同步,效率很低,
public class SingleTon3(){
private SingleTon3(){
}
private static SingleTon3 singleton3 = null;
public synchronized static getInstance(){
if(singleton3 == null){
singleton3 = new SingleTon3();
}
return singleton3 ;
}
}
優點:
- 解決單例模式執行緒安全性問題
缺點:
- 每次獲取實體時執行緒都需要同步,使得效率變低,
- 加鎖機制使得獲取物件速度較慢
2. 雙重校驗鎖模式
//安全且在多執行緒情況下能保持高性能,
//實體變數需要加volatile 關鍵字保證易變可見性
public class SingleTon4{
private SingleTon4(){
}
private volatile static SingleTon4 singleton4 = null;
public static SingleTon4 getSingleton(){
if(singleton4 == null){
synchronized (SingleTon4.class){
if(singleton4 == null){
singleton4 = new SingleTon3();
}
}
}
return singleton4 ;
}
}
優點:
- 在保證多執行緒安全情況下能保持較高性能,
缺點:
- 獲取實體時需要通過鎖機制,導致獲取物件速度較慢,
3.2餓漢式單例
優點:
- 執行緒安全
- 獲取物件實體反應速度快
缺點:
- 系統加載時間較長
4.擴展
IoDH(Initialization Demand Holder)實作單例模式
class Singleton {
private Singleton() {
}
private static class HolderClass {
private final static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return HolderClass.instance;
}
}
優點:
- 實作延遲加載
- 保證執行緒安全:相當于間接使用餓漢模式,靜態內部類一旦被加載就直接創建一個單例實體,
- 不影響系統性能:既不會在程式啟動時加載類,也不會在獲取物件時進行加鎖控制,
缺點
- 與編程語言本身的特性相關,很多面向物件語言不支持IoDH
提問
了解到IoDH這里的時候有一些小小的迷惑,靜態內部類實作延遲加載?靜態內部類不是程式啟動時就加載的嗎?帶著這個問題自己寫了一個小小的測驗代碼:
public class testStaticClass {
static class a{
static {
System.out.println("靜態內部類被加載");
}
}
static {
System.out.println("靜態代碼塊被加載");
}
public static void main(String[] args) {
}
}

可以看到在運行該程式時靜態代碼塊確實是在程式啟動時就被加載,但靜態內部類中的靜態代碼塊并沒有被加載,為了確認自己的驗證,通過查閱書籍了解到:
內部類和靜態內部類都是延時加載的,也就是說只有在明確用到內部類時才加載,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/202831.html
標籤:其他
下一篇:學習方法和學習思路的總結
