設計模式之單例模式(Java)

設計模式系列文章:

  1、設計模式之工廠模式
  2、設計模式之單例模式
  3、設計模式之代理模式
  4、設計模式之外觀模式(即門面模式)
  5、設計模式之策略模式
  6、設計模式之責任鏈模式
  7、設計模式之觀察者模式
  8、設計模式之建造者模式

設計模式是一套反覆使用的、多數人知曉的、經過分類編目的、代碼設計經驗總結。使用設計模式爲了提高代碼的可重用性,讓代碼更容易被他人理解,保證代碼的可靠性。

單例模式的概念及特點:

  java中單例模式是一種常見的設計模式,單例模式的寫法有好幾種,這裏主要介紹兩種:懶漢式單例、餓漢式單例。
  單例模式有以下特點:
  1、單例類只能有一個實例。
  2、單例類必須自己創建自己的唯一實例。
  3、單例類必須給所有其他對象提供這一實例。
  單例模式確保某個類只有一個實例,而且自行實例化並向整個系統提供這個實例。在計算機系統中,線程池、緩存、日誌對象、對話框、打印機、顯卡的驅動程序對象常被設計成單例。這些應用都或多或少具有資源管理器的功能。每臺計算機可以有若干個打印機,但只能有一個Printer Spooler,以避免兩個打印作業同時輸出到打印機中。每臺計算機可以有若干通信端口,系統應當集中管理這些通信端口,以避免一個通信端口同時被兩個請求同時調用。總之,選擇單例模式就是爲了避免不一致狀態,避免政出多頭。

一、懶漢式單例模式

package com.tong.Singleton1;
/*
懶漢式單例模式
 */
public class Singleton1 {
    //私有化構造方法
    private Singleton1() {}
    private static Singleton1 single=null;
    //靜態工廠方法   
    public static Singleton1 getInstance() {
        if (single == null) {
            single = new Singleton1();
        }
        return single;
    }
}  

Singleton通過將構造方法限定爲private避免了類在外部被實例化,在同一個虛擬機範圍內,Singleton的唯一實例只能通過getInstance()方法訪問。
但是以上懶漢式單例的實現沒有考慮線程安全問題,它是線程不安全的,併發環境下很可能出現多個Singleton實例,要實現線程安全,可以用下面兩種併發方式,都是對getInstance這個方法改造,保證了懶漢式單例的線程安全
(1) 在getInstance方法上加同步,即用synchronized 修飾方法
(2)雙重檢查鎖定

package com.tong.Singleton1;
/*
懶漢式單例模式
 */
public class Singleton1 {
    volatile private static Singleton1 singleton = null; 
    //私有化構造方法
    private Singleton1() {}
    private static Singleton1 single=null;
    //靜態工廠方法   
    public static Singleton1 getInstance() {  
        if (singleton == null) {    
            synchronized (Singleton1.class) {    
               if (singleton == null) {    
                  singleton = new Singleton();   
               }    
            }    
        }    
        return singleton;   
    } 
}  

(3)靜態內部類

public class Singleton1 {
    //靜態內部類    
    private static class LazyHolder {    
       private static final Singleton1 INSTANCE = 
               new Singleton1();    
    }    
    private Singleton1 (){}    
    public static final Singleton1 getInstance() {    
       return LazyHolder.INSTANCE;    
    }    
}  

二、餓漢式單例模式

餓漢式在類創建的同時就已經創建好一個靜態的對象供系統使用,以後不再改變,所以天生是線程安全的。

package com.tong.Singleton2;
/*
餓漢式單例模式
 */
public class Singleton2 {
    //私有化構造方法
    private Singleton2() {}
    private static final Singleton2 single = new Singleton2();
    //靜態工廠方法   
    public static Singleton2 getInstance() {
        return single;
    }
} 

三、餓漢式和懶漢式區別

(1)線程安全:
懶漢式本身是非線程安全的,爲了實現線程安全有幾種寫法,分別是上面的1、2、3,這三種實現在資源加載和性能方面有些區別。
餓漢式天生就是線程安全的,可以直接用於多線程而不會出現問題,

(2)資源加載和性能:
懶漢式顧名思義,會延遲加載,在第一次使用該單例的時候纔會實例化對象出來,第一次調用時要做初始化,如果要做的工作比較多,性能上會有些延遲,之後就和餓漢式一樣了。
至於1、2、3這三種實現又有些區別,
第1種,在方法調用上加了同步,雖然線程安全了,但是每次都要同步,會影響性能,畢竟99%的情況下是不需要同步的,
第2種,在getInstance中做了兩次null檢查,確保了只有第一次調用單例的時候纔會做同步,這樣也是線程安全的,同時避免了每次都同步的性能損耗
第3種,利用了classloader的機制來保證初始化instance時只有一個線程,所以也是線程安全的,同時沒有性能損耗,所以一般比較傾向於使用這一種。
餓漢式在類創建的同時就實例化一個靜態對象出來,不管之後會不會使用這個單例,都會佔據一定的內存,但是相應的,在第一次調用時速度也會更快,因爲其資源已經初始化完成,

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章