設計模式之單例設計模式詳細解剖及源碼分析

單例設計模式分爲:八種

  1. 靜態常量餓漢式
  2. 靜態代碼塊餓漢式
  3. 線程不安全懶漢式
  4. 線程安全懶漢式
  5. 同步代碼塊懶漢式
  6. DoubleCheck(雙重鎖)
  7. 靜態內部類
  8. 枚舉方式ENUM
  •  靜態常量餓漢式

/**
 * @ClassName SingleTest01
 * @Description 單列模式-餓漢式-靜態常量
 * @Author Chao.Qin
 * @Datw 2019/11/28 10:46
 */
public class SingleTest01 {

    private SingleTest01(){

    }
    private static final SingleTest01 singleTest = new SingleTest01();

    public static SingleTest01 getInstance(){
        return singleTest;
    }
}

class SinTest01{
    public static void main(String[] args) {
        SingleTest01 singleTest01 = SingleTest01.getInstance();
        SingleTest01 singleTest02 = SingleTest01.getInstance();
        System.out.println(singleTest01==singleTest02);
    }
}
  • 靜態常量餓漢式優缺點說明
  1. 優點:寫法簡單,在類裝載的時候完成實列化。避免了線程同步問題
  2. 缺點:在類裝載的時候就完成實例化,沒有達到Lazy Loading(懶加載)的效果,如果從始至終沒有使用該類,就會造成內存浪費
  3. 這種方式基於classloder機制避免的線程同步問題,不過,instance在類裝載時就進行實例化,在單例模式中大多數都調用了getInstance方法,但導致類裝載的原因有很多種,因此不能確定有其他方式(或者其他靜態方法)導致類裝載,這時候初始化instance就沒有達到懶加載的效果
  4. 結論:此單例方法可用,可能會造成內存浪費 
  •  靜態代碼塊餓漢式

/**
 * @ClassName SingleTest02
 * @Description 單列模式-餓漢式-靜態代碼塊
 * @Author Chao.Qin
 * @Datw 2019/11/28 10:50
 */
public class SingleTest02 {

    private SingleTest02(){

    }
    private static SingleTest02 instance;

    static {
        instance = new SingleTest02();
    }

    public static SingleTest02 getInstance(){
        return instance;
    }

}

class SinTest02{
    public static void main(String[] args) {
        SingleTest02 singleTest01 = SingleTest02.getInstance();
        SingleTest02 singleTest02 = SingleTest02.getInstance();
        System.out.println(singleTest01==singleTest02);
    }
}
  • 靜態代碼塊餓漢式優缺點說明
  1.  這種方式和靜態常量餓漢式類似,只不過將類實例化的過程放在了靜態代碼塊中,也是在類裝載的時候就執行了靜態代碼塊中的代碼,初始化實例,和靜態常量餓漢式的優缺點一樣
  2. 結論:此單例方法可用,可能會造成內存浪費 
  •  線程不安全懶漢式

/**
 * @ClassName SingleTest03
 * @Description 線程不安全懶漢式
 * @Author Chao.Qin
 * @Datw 2019/12/12 14:29
 */
public class SingleTest03 {

    private static SingleTest03 instance;

    private SingleTest03(){}

    //提供靜態的公有方法,當使用到該方式時,纔去創建instance-----即懶漢式
    public static SingleTest03 getInstance(){
        if (instance == null){
            instance = new SingleTest03();
        }
        return instance;
    }

}
class SinTest03{
    public static void main(String[] args) {
        SingleTest03 instance = SingleTest03.getInstance();
        SingleTest03 instance1 = SingleTest03.getInstance();
        System.out.println(instance==instance1);
    }
}
  • 線程不安全懶漢式優缺點說明
  1.  起到了Lazy Loading(懶加載)的效果,但只能在單線程下使用
  2. 如果在多線程下,一個線程進入if(instance==null)判斷語句塊,還未來的及往下執行,另一個線程也通過了該判斷語句,這時就會產生多個實例,所以在多線程環境下不可以使用該方式
  3. 結論:在實際開發中,不推薦使用該方法
  •  線程安全懶漢式

/**
 * @ClassName SingleTest04
 * @Description 線程安全懶漢式
 * @Author Chao.Qin
 * @Datw 2019/12/12 15:08
 */
public class SingleTest04 {
    private static SingleTest04 instance;

    private SingleTest04(){}

    // 提供靜態的公有方法,加入同步處理,解決線程安全問題
    public static synchronized SingleTest04 getInstance(){
        if (instance == null){
            instance = new SingleTest04();
        }
        return instance;
    }

}
class SinTest04{
    public static void main(String[] args) {
        SingleTest04 instance = SingleTest04.getInstance();
        SingleTest04 instance1 = SingleTest04.getInstance();
        System.out.println(instance==instance1);
    }
}
  •  線程安全懶漢式優缺點說明
  1.  解決了線程不安全問題
  2. 效率太低了,每個線程想獲取實例化對象時,執行getInstance()方法都要進行同步,而其實這種方式只需要執行一次實例化即可,後面想獲取實例化直接return就行。該方法同步效率太低
  3. 結論:在實際開發中,不推薦使用該方法
  •  同步代碼塊懶漢式

/**
 * @ClassName SingleTest05
 * @Description 同步代碼塊懶漢式
 * @Author Chao.Qin
 * @Datw 2019/12/12 15:08
 */
public class SingleTest05 {
    private static SingleTest05 instance;

    private SingleTest05() {
    }

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

}

class SinTest05 {
    public static void main(String[] args) {
        SingleTest05instance = SingleTest05.getInstance();
        SingleTest05instance1 = SingleTest05.getInstance();
        System.out.println(instance == instance1);
    }
}
  •  同步代碼塊懶漢式優缺點說明
  1.  該種方式:對線程安全懶漢式進行改造,因爲前一種方式效率低,改成同步代碼塊的形式
  2. 但這種方式並不能起到線程同步的作用,跟線程不安全懶漢式實際方式一致,如果一個線程進入if(instance==null)判斷語句塊,還未來的及往下執行,另一個線程也通過了該判斷語句,這時就會產生多個實例,所以在多線程環境下不可以使用該方式
  3. 結論:在實際開發中,不推薦使用該方法
  •  DoubleCheck(雙重鎖)雙重檢查

/**
 * @ClassName SingleTest06
 * @Description 雙重檢查
 * @Author Chao.Qin
 * @Datw 2019/12/12 15:08
 */
public class SingleTest06 {
    private static volatile SingleTest06 instance;

    private SingleTest06() {
    }

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

}

class SinTest06 {
    public static void main(String[] args) {
        SingleTest06 instance = SingleTest06.getInstance();
        SingleTest06 instance1 = SingleTest06.getInstance();
        System.out.println(instance == instance1);
    }
}
  •   DoubleCheck(雙重鎖)雙重檢查​​​​​​​​​​​​​​優缺點說明
  1.  DoubleCheck是多線程開發中使用的,如代碼所示,進行兩次判斷if(instance==null)檢查,這樣既可以保證線程安全,又可以保證對象實例化一次
  2. 實例化代碼只會執行一次,後面再訪問時,判斷語句就會直接return實例化對象,避免了多次創建實例化對像,反覆進行方法同步
  3. 線程安全,延遲加載,效率高
  4. 結論:推薦再實際開發中使用
  •  靜態內部類

    /**
     * @ClassName SingleTest07
     * @Description 靜態內部類
     * @Author Chao.Qin
     * @Datw 2019/12/12 15:08
     */
    public class SingleTest07 {
        private static volatile SingleTest07 instance;
    
        private SingleTest07() {
        }
    
        private static class SingleInstance {
    
            private static final SingleTest07 INSTANCE = new SingleTest07();
    
        }
    
        public static SingleTest07 getInstance(){
            return SingleInstance.INSTANCE;
        }
    
    }
    
    class SinTest7 {
        public static void main(String[] args) {
            SingleTest07 instance = SingleTest07.getInstance();
            SingleTest07 instance1 = SingleTest07.getInstance();
            System.out.println(instance == instance1);
        }
    }

     

  • 靜態內部類​​​​​​​​​​​​​​優缺點說明
  1.  這種方式採用了類裝載的機制來保證初始化實例只有一個線程
  2. 靜態內部類在類裝載時並不會立即實例化,而是在需要實例化時,調用SingleInstance方法纔會裝載該類,從而完成SingleTest7的實例化
  3. 類的靜態屬性只會在第一次加載的時候進行初始化,所以在這裏,JVM保證了線程安全性,在類進行初始化時,只會有一個線程進行,別的線程無法進入
  4. 優點:避免了線程不安全,利用了靜態內部類的特點實現了延遲加載,效率高
  5. 結論:推薦使用
  •  枚舉類

enum Singletest8{
     INSTANCE;
    public void method(){
    }
}
  •  枚舉優缺點說明
  1. JDK1.5添加枚舉類,實現單例模式,不僅避免了多線程同步問題,從而還能防止反序列化重新創建新的對象
  2. 這種方式是Effective Java 作者JSON Bloch 提倡的方式
  3. 結論:推薦使用
  • 單例模式注意事項及細節說明 

  1. 單例模式保證了 系統內存中該類只存在一個對象,節省系統資源,對於一些頻繁創建銷燬的對象,使用單例可以提高系統的性能
  2. 當想實例化一個單例類的時候,必須要記住使用相應的獲取對象的方法,而不是使用new
  3. 單例模式使用的場景:需要頻繁創建和銷燬的對象,創建對象時耗時過多和耗費資源過多(即:重量級對象),但又經常使用的對象,工具類對象,頻繁訪問數據庫或文件對象(比如:數據源,session工廠等) 
發佈了44 篇原創文章 · 獲贊 10 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章