單例模式之懶漢式和餓漢式


一、單例模式定義

單例模式確保某個類只有一個實例,而且自行實例化並向整個系統提供這個實例。在計算機系統中,線程池、緩存、日志對象、對話框、打印機、顯卡的驅動程序對象常被設計成單例。這些應用都或多或少具有資源管理器的功能。每台計算機可以有若干個打印機,但只能有一個Printer Spooler,以避免兩個打印作業同時輸出到打印機中。每台計算機可以有若干通信端口,系統應當集中管理這些通信端口,以避免一個通信端口同時被兩個請求同時調用。總之,選擇單例模式就是為了避免不一致狀態,避免政出多頭。

 二、單例模式特點
  1、單例類只能有一個實例。
  2、單例類必須自己創建自己的唯一實例。
  3、單例類必須給所有其他對象提供這一實例。

單例模式保證了全局對象的唯一性,比如系統啟動讀取配置文件就需要單例保證配置的一致性。

 三、線程安全的問題

一方面在獲取單例的時候,要保證不能產生多個實例對象;

另一方面,在使用單例對象的時候,要注意單例對象內的實例變量是會被多線程共享的,推薦使用無狀態的對象,這樣就不會因為多個線程的交替調度而破壞自身狀態導致線程安全問題,比如我們常用的VO,DTO等(局部變量是在用戶棧中的,而且用戶棧本身就是線程私有的內存區域,所以不存在線程安全問題)。

 四、實現單例模式的方式

1、懶漢式單例(延遲加載方式)

public class Singleton {
//私有構造方法
private Singleton() {}
private static Singleton singleton = null;
//靜態工廠方法
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}

該示例雖然用延遲加載方式實現了懶漢式單例,

但在多線程環境下會產生多個single對象,會出現線程安全的問題。

將以上懶漢式單例模式改造如下:使用synchronized同步鎖

public class Singleton {
    // 私有構造
    private Singleton() {}
    private static Singleton single = null;
    public static Singleton getInstance() {
        // 等同於synchronized public static Singleton getInstance()
        synchronized(Singleton.class){
        // 注意:里面的判斷是一定要加的,否則出現線程安全問題
            if(single == null){
                single = new Singleton();
            }
        }
        return single;
    }
}
在方法上加synchronized同步鎖或是用同步代碼塊對類加同步鎖,
此種方式雖然解決了多個實例對象問題,但是該方式運行效率卻很低下,
下一個線程想要獲取對象,就必須等待上一個線程釋放鎖之后,才可以繼續運行。
如下改造優化:
public class Singleton {
    // 私有構造
    private Singleton() {}
    private static Singleton single = null;
    // 雙重檢查
    public static Singleton getInstance() {
        if (single == null) {
            synchronized (Singleton.class) {
                if (single == null) {
                    single = new Singleton();
                }
            }
        }
        return single;
    }
}

使用雙重檢查進一步做了優化,可以避免整個方法被鎖,只對需要鎖的代碼部分加鎖,可以提高執行效率。

 2、餓漢式單例(立即加載方式)

public class Singleton {  
   //私有構造方法
private Singleton(){}
private static final Singleton singleton=new Singleton();
  //靜態工廠方法
public static Singleton getInstance(){
return singleton;
}
}

餓漢式單例在類加載初始化時就創建好一個靜態的對象供外部使用,除非系統重啟,這個對象不會改變,所以本身就是線程安全的。Singleton通過將構造方法限定為private避免了類在外部被實例化,在同一個虛擬機范圍內,Singleton的唯一實例只能通過getInstance()方法訪問。(事實上,通過Java反射機制是能夠實例化構造方法為private的類的,那基本上會使所有的Java單例實現失效。此問題在此處不做討論,姑且閉着眼就認為反射機制不存在。)

詳細參見博文: http://www.cnblogs.com/garryfu/p/7976546.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM