設計模式(1)_單例模式

設計模式(1)_單例模式

設計模式經典的常用的就23種,單例模式算是 最好容易理解的設計模式之一了。但是要寫出高效、安全、真正意義上的單例模式並不容易:

單例模式有兩種實現方法:延遲實例化 和急切實例化

第一種:延遲實例化

顧名思義,延遲實例化,就是在使用時,纔去實例化對象;

package com.test;

/**
 * 設計模式之 單例模式,
 * 就是要保證一個類有且只能存在一個實例對象
 * @author crg
 *
 */
public class Singleton {
    private static Singleton uniqueInstance;

    /**
     *  構造方法私有化,外部不能訪問
     */
    private Singleton(){}

    /**
     * 向外部提供獲取該類實例的靜態方法
     * @return
     */
    public static Singleton getInstance(){
        if (uniqueInstance == null) {

            //該類對象不存在 才創建實例對象,如果存在直接返回該
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}

一般都是這麼寫的。但是這真的是單例嗎?下面測試一下:
測試代碼:

package com.test;

public class MainTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                // 獲得實例對象
                Singleton mSingleton = Singleton.getInstance();

                //打印出對象的內存地址
                System.out.println(mSingleton);
            }
        }).start();

        new Thread(new Runnable() {

            @Override
            public void run() {
                Singleton mSingleton = Singleton.getInstance();
                System.out.println(mSingleton);
            }
        }).start();

        new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Singleton mSingleton = Singleton.getInstance();
                System.out.println(mSingleton);
            }
        }).start();
    }

}

測試結果:

com.test.Singleton@3ce53108
com.test.Singleton@6af62373
com.test.Singleton@6af62373
com.test.Singleton@2e6e1408
com.test.Singleton@3ce53108
com.test.Singleton@3ce53108

很明顯這不是單例模式,因爲同時存在兩個不同的對象實例。原因是:這種寫法在多線程環境下是不安全的,可能會同時多個線程同時訪問 getInstance() 方法,就像剛纔的測試結果。

通過 synchronized 關鍵字,同步 getInstance() 該方法,可以解決該問題,使線程安全,即可以實現單例模式。

/**
     * 向外部提供獲取該類實例的靜態方法
     * @return
     */
    public static synchronized Singleton getInstance(){
        if (uniqueInstance == null) {

            //該類對象不存在 才創建實例對象,如果存在直接返回該
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }

結果如下:

com.test.Singleton@6af62373
com.test.Singleton@6af62373
com.test.Singleton@6af62373

但是 這樣做,雖然保證了安全,但是使性能降低,同步極大的影響性能。關鍵是隻有當 uniqueInstance = new Singleton() 第一次執行時,才需要同步。
解決辦法如下,使用雙重檢查加鎖同步代碼塊:

 /**
     *  延遲實例化 在多線程環境下,爲了安全性,需要同步,降低了性能,
     *  以空間換時間
     */
    /**
     *  volatile 關鍵字保證 uniqueInstance 線程之間的可見性
     */
    private volatile static Singleton uniqueInstance;

    /**
     *  構造方法私有化,外部不能訪問
     */
    private Singleton(){}

    /**
     * 向外部提供獲取該類實例的靜態方法
     * @return
     */
    public static Singleton getInstance(){
        if (uniqueInstance == null) {

            synchronized (Singleton.class) {
                if (uniqueInstance == null) {

                  //該類對象不存在 才創建實例對象,如果存在直接返回該
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }

二,急切實例化

private static Singleton uniqueInstance = new Singleton();
  /**
   *  急切實例化 隨着類的加載 jvm 已經創建了 uniqueInstance 
   *  保證了安全性,也提高了性能
   *  但是,佔用空間,即便在整個生命週期中,一此不使用 uniqueInstance,uniqueInstance 對象也會一直在內存中
   *   是以空間換時間的解決辦法
   *  構造方法私有化,外部不能訪問
   */
  private Singleton(){}

  public static Singleton getInstance(){

        return uniqueInstance;
  }

JVM 在加載這個類時,馬上創建此唯一的單件實例。

三,最優方案

/**
     *  時間和空間的方案
     */

    /** 
     *  私有構造方法,禁止外部訪問
     */
    private Singleton(){}

    /**
     *  靜態內部類
     * @author crg
     *
     */
    private static class HolderClass{
        private static final Singleton uniqueInstance = new Singleton();
    }
    public static Singleton getInstance(){
        return HolderClass.uniqueInstance;
    }

源碼地址:https://github.com/aloe-all/DesignPattern_Singleton

發佈了29 篇原創文章 · 獲贊 16 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章