1. 程式人生 > >設計模式的難點---單例模式

設計模式的難點---單例模式

單例模式在設計模式的模式使用上很簡單但是博主的意思單例模式的使用需要很深的java基礎;這是小編認為單例模式的難點;

定義

單例模式(Singleton Pattern):確保某一個類只有一個例項,而且自行例項化並向整個系統提供這個例項,這個類稱為單例類,它提供全域性訪問的方法。單例模式是一種物件建立型模式。

要點

單例模式有三個要點:
一是某個類只能有一個例項;
二是它必須自行建立這個例項;
三是它必須自行向整個系統提供這個例項。自行提供的時機可以是類載入的時機。也可以是在使用這個類的時候
這裡寫圖片描述

Singleton(單例):在單例類的內部實現只生成一個例項,同時它提供一個靜態的
getInstance()工廠方法,讓客戶可以訪問它的唯一例項;為了防止在外部對其例項化,將其建構函式設計為私有;在單例類內部定義了一個Singleton型別的靜態物件,作為外部共享的唯一例項。

java中的實現方式

1.餓漢式單例

由於在定義靜態變數的時候例項化單例類,因此在類載入的時候就已經
建立了單例物件,程式碼如下所示:

class EagerSingleton {
        private static final EagerSingleton instance = new EagerSingleton();
        private EagerSingleton() { }
    public static EagerSingleton getInstance() {
        return instance;
    }
}

當類被載入時,靜態變數instance會被初始化,此時類的私有建構函式會被呼叫,單例類的唯一例項將被建立。如果使用餓漢式單例來實現負載均衡器LoadBalancer類的設計,則不會出現建立多個單例物件的情況,可確保單例物件的唯一性.

優點

在類的載入時機類物件已經被建立所以避免的多執行緒問題:

問題

由於在載入時機建立並賦值物件。造成載入花費的時間大。

2.懶漢式單例

懶漢式單例在第一次呼叫getInstance()方法時例項化,在類載入時並不自
行例項化,這種技術又稱為延遲載入(Lazy Load)技術,即需要的時候再載入例項,為了避免多個執行緒同時呼叫getInstance()方法,我們可以使用關鍵字synchronized。

class LazySingleton {
    private static LazySingleton instance = null;
    private LazySingleton() { }
    synchronized public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

缺點

每次呼叫getInstance()時都需要進行執行緒鎖定判斷,在多執行緒高併發訪問環境中,將會導致系統性能大大降低。
改進方式:

雙重檢查鎖定

class LazySingleton {
    private volatile static LazySingleton instance = null;
    private LazySingleton() { }
        public static LazySingleton getInstance() {
        //第一重判斷
        if (instance == null) {
            //鎖定程式碼塊
            synchronized (LazySingleton.class) {
            //第二重判斷
                if (instance == null) { 
                    instance = new LazySingleton(); //建立單例例項
                }
            }
        }
    return instance;
    }
}

如果使用雙重檢查鎖定來實現懶漢式單例類,需要在靜態成員變數instance之
前增加修飾符volatile,被volatile修飾的成員變數可以確保多個執行緒都能夠正確處理,且該程式碼只能在JDK 1.5及以上版本中才能正確執行。由於volatile關鍵字會遮蔽Java虛擬機器所做的一些程式碼優化,可能會導致系統執行效率降低,因此即使使用雙重檢查鎖定來實現單例模式也不是一種完美的實現方式。
這裡的volatile 的雙重檢測機制的在jdk 1.5之間有bug.在1.5後修復了。這是小編在《深入理解java虛擬機器》中看到的。這裡的volatile 的有兩個作用;其中的一個作用在java虛擬機器中就是指的指令重排序問題。

兩種載入模式的 比較

摘自:java設計模式

.餓漢式單例類與懶漢式單例類比較
餓漢式單例類在類被載入時就將自己例項化,它的優點在於無須考慮多執行緒訪問問題,可以確保例項的唯一性;從呼叫速度和反應時間角度來講,由於單例物件一開始就得以建立,因此要優於懶漢式單例。但是無論系統在執行時是否需要使用該單例物件,由於在類載入時該物件就需要建立,因此從資源利用效率角度來講,餓漢式單例不及懶漢式單例,而且在系統載入時由於需要建立餓漢式單例物件,載入時間可能會比較長。
懶漢式單例類在第一次使用時建立,無須一直佔用系統資源,實現了延遲載入,但是必須處理好多個執行緒同時訪問的問題,特別是當單例類作為資源控制器,在例項化時必然涉及資源初始化,而資源初始化很有可能耗費大量時間,這意味著出現多執行緒同時首次引用此類的機率變得較大,需要通過雙重檢查鎖定等機制進行控制,這將導致系統性能受到一定影響。

缺點是:
一個是:無論用不用到都會建立導致載入時間長。
另一個是:多執行緒問題導致效能下降。

使用內部類

Initialization Demand Holder (IoDH)的技術
在IoDH中,我們在單例類中增加一個靜態(static)內部類,在該內部類中建立單例物件,再將該單例物件通過getInstance()方法返回給外部使用,實現程式碼如下所示:

//Initialization on Demand Holder
class Singleton {
    private Singleton() {

    }
    private static class HolderClass {
        private final static Singleton instance = new Singleton();
    }
    public static Singleton getInstance() {
        return HolderClass.instance;
    }
    public static void main(String args[]) {
        Singleton s1, s2;
        s1 = Singleton.getInstance();
        s2 = Singleton.getInstance();
        System.out.println(s1==s2);
    }
}

現在你在看看菜鳥教程的是不是一目瞭然,還是那句話。想學好習,不要看什麼菜鳥教程的垃圾東西。