Java 設計模式——單例模式 理論代碼相結合

 1、前言

概念:

單例模式,屬於建立類型的一種經常使用的軟件設計模式。經過單例模式的方法建立的類在當前進程中只有一個實例(根據須要,也有可能一個線程中屬於單例,如:僅線程上下文內使用同一個實例)--來自百度百科數據庫

應用:

單例模式可讓咱們只建立一個對象從而避免了頻繁建立對象致使的內存消耗和垃圾回收。設計模式

單例模式只容許建立一個對象,所以節省內存,加快對象訪問速度,所以對象須要被公用的場合適合使用,如多個模塊使用同一個數據源鏈接對象等等。如:安全

  1. 須要頻繁實例化而後銷燬的對象。
  2. 建立對象時耗時過多或者耗資源過多,但又常常用到的對象。
  3. 有狀態的工具類對象。
  4. 頻繁訪問數據庫或文件的對象。

項目中的具體應用:多線程

  1. 封裝一些經常使用的工具類,保證整個應用經常使用的數據統一
  2. 保存一些共享數據在內存中,其餘類隨時能夠讀取。

實現單例模式的原則和過程:

  1. 單例模式:確保一個類只有一個實例,自行實例化並向系統提供這個實例
  2. 單例模式分類:餓單例模式(類加載時實例化一個對象給本身的引用),懶單例模式(調用取得實例的方法如getInstance時纔會實例化對象)
  3. 單例模式要素: a.私有構造方法 b.私有靜態引用指向本身實例 c.以本身實例爲返回值的公有靜態方法

方式:

單例模式有八種方式:ide

  1. 餓漢式(靜態常量)
  2. 餓漢式(靜態代碼塊)
  3. 懶漢式(線程不安全方式)
  4. 懶漢式(線程安全,同步方法)
  5. 懶漢式(線程不安全,同步代碼塊)
  6. 懶漢式(雙重檢查)
  7. 懶漢式(靜態內部類)
  8. 枚舉實現
2、單例模式代碼實現及分析

2.一、餓漢式(靜態常量)

代碼實現:工具

/**
 * 單例模式
 * @Author: crush
 * @Date: 2021-08-06 9:14
 * version 1.0
 */
public class SingletonTest1 {
    public static void main(String[] args) {
        // 獲取兩次,看獲取到的對象 確實是單例的嗎
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();

        System.out.println(singleton == singleton1);

        System.out.println("singleton  hashcode:"+singleton.hashCode());
        System.out.println("singleton1 hashcode:"+singleton1.hashCode());
        /**
         * 輸出:
         * true
         * singleton  hashcode:24324022
         * singleton1 hashcode:24324022
         */
    }
}

/**
 *  一、餓漢式(靜態常量)代碼實現
 */
class Singleton{
    /*** 構造器私有化*/
    private Singleton(){};
    
    /** * 在類的內部建立一個對象實例 隨當前類加載而加載 沒有線程安全問題。 */
    private final static Singleton INSTANCE=new Singleton();
    
    /*** 再提供一個 公有的方法來返回這個靜態常量*/
    public static Singleton getInstance(){
        return INSTANCE;
    }
}

結論及優缺測試

  1. 優勢:餓漢式(靜態常量方式)它不用擔憂線程安全問題,它自己就是在類的裝載的時候完成實例化的。spa

  2. 缺點:可是缺點也在這個地方,無論用不用,只要類裝載了,那麼他就會直接完成實例化,不能達到懶加載的效果,若是你從始至終都沒有使用過這個類,那麼就會形成內存的浪費。線程

    注意:你可能會想類都已經加載了,爲何我還會不用到呢?設計

    在單例模式中大都調用getInstance方法,getInstance這個靜態方法可讓類加載,那麼一樣的道理,若是這個類中還有其餘的靜態方法,你調用它的時候,一樣會使類進行裝載。

  3. 小結:這種單例方式,其實仍是能夠用的,至少不用擔憂線程同步問題,那種使用特別頻繁的類用這種方式仍是沒啥問題的,除了可能會致使內存浪費

2.二、餓漢式(靜態代碼塊)

/**
 * 單例模式 2
 *
 * @Author: crush
 * @Date: 2021-08-06 9:14
 * version 1.0
 */
public class SingletonTest1 {
    public static void main(String[] args) {
        // 咱們去拿兩次,看獲取到的對象 確實是單例的嗎
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();

        System.out.println(singleton == singleton1);

        System.out.println("singleton  hashcode:" + singleton.hashCode());
        System.out.println("singleton1 hashcode:" + singleton1.hashCode());
        /**
         * 輸出:
         * true
         * singleton  hashcode:24324022
         * singleton1 hashcode:24324022
         */
    }
}

/**
 * 一、餓漢式(靜態代碼塊)代碼實現
 */
class Singleton {

    /** * 構造器私有化 */
    private Singleton() {
    }
    /** * 在類的內部建立一個對象實例 隨當前類加載而加載 沒有線程安全問題。*/
    private static Singleton singleton;

    static {
        //改成在靜態代碼塊中 建立單例對象
        singleton = new Singleton();
    }

    /** * 再提供一個 公有的方法來返回這個靜態常量  */
    public static Singleton getInstance() {
        return singleton;
    }
}

結論:這種方式其實和第一種很是相似,只是將類的實例化過程放進靜態代碼塊而已。優缺點同餓漢式(靜態常量)。

2.三、懶漢式(線程不安全)

/**
 * 單例模式
 *
 * @Author: crush
 * @Date: 2021-08-06 9:14
 * version 1.0
 */
public class SingletonTest3 {
    public static void main(String[] args) {

        //懶漢式  線程不安全方式,適合單線程使用
        //===========單線程下是安全的 ,測試代碼和第一種同樣===========
        // =========模擬多線程下=============
        Runnable runnable = new Runnable(){
            @Override
            public void run() {
                Singleton instance = Singleton.getInstance();
                System.out.println(instance.hashCode());
            }
        };

        Runnable runnable2 = new Runnable(){
            @Override
            public void run() {
                Singleton instance1 = Singleton.getInstance();
                System.out.println(instance1.hashCode());
            }
        };
        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable2);
        thread1.start();
        thread2.start();
        /**
         * 結果並不惟一,
         * 可能會出現相同,也有可能不一樣,多測幾回,就能發現是線程不安全的。
         * 94433
         * 21648409
         */
    }
}

/**
 * 懶漢式 線程不安全方式
 */
class Singleton {

    /*** 構造器私有化*/
    private Singleton() {}
    
    /*** 在類的內部建立一個對象實例 隨當前類加載而加載 沒有線程安全問題。*/
    private static Singleton singleton;

    /**
     * 提供一個公有的方法
     * 當使用到這個方法時,纔去建立singleton
     */
    public static Singleton getInstance() {
        if(singleton==null){
            // 經過在這裏堵賽的方式來模擬多線程
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            singleton= new Singleton();
        }
        return singleton;
    }
}

結論及優缺:

  1. 優勢:起到了懶加載的效果
  2. 缺點:線程不安全,若是在多線程下,第一個線程進入到if(singleton==null)下,但還將來的及執行,第二個線程就緊隨而來也進入了if(singleton==null)下,那麼就會建立多個實例。就不是單例模式了。
  3. 建議:開發不要使用這種方式,線程安全問題不能解決,就是
相關文章
相關標籤/搜索