电竞比分网-中国电竞赛事及体育赛事平台

分享

創(chuàng)建型設(shè)計模式--單例模式

 路人甲Java 2020-06-28

一、單例模式

1、什么是單例模式

  采取一定的方法,使程序中的某個類只存在一個實例對象,且該類對外提供一個獲取該對象的方法(一般為靜態(tài)方法)。

2、單例模式分類

(1)餓漢式(2種寫法,線程安全)
  靜態(tài)變量
  靜態(tài)代碼塊

(2)懶漢式(3種寫法)
  線程不安全
  線程安全,同步方法
  線程安全,同步代碼塊(不推薦使用)

(3)雙重檢查(推薦使用)
(4)靜態(tài)內(nèi)部類(推薦使用)
(5)枚舉(推薦使用)

3、餓漢式單例模式(靜態(tài)常量版)

(1)步驟:
  step1:構(gòu)造器私有化(防止通過new創(chuàng)建實例對象)
  step2:在類的內(nèi)部創(chuàng)建實例對象。
  step3:向外暴露一個靜態(tài)的公共方法用于獲取實例對象。
(2)代碼實現(xiàn):

package singleton.pattern.demo1;

/**
 * 演示 餓漢式單例模式,靜態(tài)變量版
 *
 */
public class Demo {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleTon();
        Singleton singleton2 = Singleton.getSingleTon();
        System.out.println(singleton1 == singleton2); // 由于獲取的為同一個對象,所以輸出為true
    }
}

/**
 * 餓漢式單例模式,靜態(tài)變量版
 */
class Singleton {
    // 在類的內(nèi)部創(chuàng)建實例對象。使用靜態(tài)變量,只被加載一次。
    private static Singleton singleton = new Singleton();

    /**
     * 構(gòu)造器私有化(防止通過new創(chuàng)建實例對象)
     */
    private Singleton() {
    }

    /**
     * 向外暴露一個靜態(tài)的公共方法用于獲取實例對象。
     * 
     * @return 實例對象
     */
    public static Singleton getSingleTon() {
        return singleton;
    }
}

(3)優(yōu)缺點:
  優(yōu)點:寫法簡單,在類裝載時完成了實例化,避免線程同步問題。
  缺點:在類裝載前完成實例化,沒有實現(xiàn)懶加載(Lazy Loading),可能造成內(nèi)存的浪費(比如從不使用該類時會造成內(nèi)存的浪費)。

(4)UML圖:

 

4、餓漢式單例模式(靜態(tài)代碼塊)

(1)步驟:
  step1:構(gòu)造器私有化(防止通過new創(chuàng)建實例對象)
  step2:在類的內(nèi)部聲明實例對象,并在靜態(tài)代碼塊中實例化對象。
  step3:向外暴露一個靜態(tài)的公共方法用于獲取實例對象。
(2)代碼實現(xiàn):

package singleton.pattern.demo2;

/**
 * 演示 餓漢式單例模式,靜態(tài)代碼塊版
 *
 */
public class Demo {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleTon();
        Singleton singleton2 = Singleton.getSingleTon();
        System.out.println(singleton1 == singleton2); // 由于獲取的為同一個對象,所以輸出為true
    }
}

/**
 * 餓漢式單例模式,靜態(tài)代碼塊版
 */
class Singleton {
    // 在類的內(nèi)部聲明一個實例對象
    private static Singleton singleton;

    static {
        // 在代碼塊中實例化一個對象,同樣只加載一次
        singleton = new Singleton();
    }

    /**
     * 構(gòu)造器私有化(防止通過new創(chuàng)建實例對象)
     */
    private Singleton() {
    }

    /**
     * 向外暴露一個靜態(tài)的公共方法用于獲取實例對象。
     * 
     * @return 實例對象
     */
    public static Singleton getSingleTon() {
        return singleton;
    }
}

(3)優(yōu)缺點同上例 餓漢式單例模式(靜態(tài)常量版)

5、懶漢式單例模式(線程不安全)

(1)步驟:
  step1:構(gòu)造器私有化(防止通過new創(chuàng)建實例對象)
  step2:在類的內(nèi)部聲明實例對象。
  step3:向外暴露一個靜態(tài)的公共方法用于獲取實例對象,在調(diào)用該方法時,才去創(chuàng)建實例對象。
(2)代碼實現(xiàn):

package singleton.pattern.demo3;

/**
 * 演示 懶漢式單例模式,線程不安全版
 *
 */
public class Demo {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleTon();
        Singleton singleton2 = Singleton.getSingleTon();
        System.out.println(singleton1 == singleton2); // 由于獲取的為同一個對象,所以輸出為true
    }
}

/**
 * 懶漢式單例模式,線程不安全版
 */
class Singleton {
    // 在類的內(nèi)部聲明一個實例對象
    private static Singleton singleton;

    /**
     * 構(gòu)造器私有化(防止通過new創(chuàng)建實例對象)
     */
    private Singleton() {
    }

    /**
     * 向外暴露一個靜態(tài)的公共方法用于獲取實例對象。 當調(diào)用該方法時,才去檢查并創(chuàng)建一個實例對象。
     * 
     * @return 實例對象
     */
    public static Singleton getSingleTon() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

(3)優(yōu)缺點:
  優(yōu)點:實現(xiàn)了懶加載。
  缺點:只能在單線程下使用,比如線程A與線程B并發(fā)執(zhí)行到 if (singleton == null), 此時便會產(chǎn)生多個實例對象。

6、懶漢式單例模式(線程安全,同步方法)

(1)步驟:
  step1:構(gòu)造器私有化(防止通過new創(chuàng)建實例對象)
  step2:在類的內(nèi)部聲明實例對象。
  step3:向外暴露一個靜態(tài)的公共方法(給靜態(tài)方法加個synchronized關(guān)鍵字,解決同步問題)用于獲取實例對象,在調(diào)用該方法時,才去創(chuàng)建實例對象。
(2)代碼實現(xiàn):

package singleton.pattern.demo4;

/**
 * 演示 懶漢式單例模式,線程安全,同步方法版
 *
 */
public class Demo {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleTon();
        Singleton singleton2 = Singleton.getSingleTon();
        System.out.println(singleton1 == singleton2); // 由于獲取的為同一個對象,所以輸出為true
    }
}

/**
 * 懶漢式單例模式,線程安全,同步方法版
 */
class Singleton {
    // 在類的內(nèi)部聲明一個實例對象
    private static Singleton singleton;

    /**
     * 構(gòu)造器私有化(防止通過new創(chuàng)建實例對象)
     */
    private Singleton() {
    }

    /**
     * 向外暴露一個靜態(tài)的公共方法用于獲取實例對象,并給方法加個synchronized關(guān)鍵字,解決同步的問題。
     * 當調(diào)用該方法時,才去檢查并創(chuàng)建一個實例對象。
     * 
     * @return 實例對象
     */
    public static synchronized Singleton getSingleTon() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

(3)優(yōu)缺點:
  優(yōu)點:實現(xiàn)了懶加載,解決了線程不安全的問題。
  缺點:效率太低,每個線程想獲取實例對象時,均會觸發(fā)同步方法,此時會等待前一個線程調(diào)用結(jié)束后才能使用,使效率低。

 

7、懶漢式單例模式(同步代碼塊方法,線程不一定安全)

(1)步驟:
  step1:構(gòu)造器私有化(防止通過new創(chuàng)建實例對象)
  step2:在類的內(nèi)部聲明實例對象。
  step3:向外暴露一個靜態(tài)的公共方法(在靜態(tài)方法內(nèi)部定義一個代碼塊,解決同步問題)用于獲取實例對象,在調(diào)用該方法時,才去創(chuàng)建實例對象。
(2)代碼實現(xiàn):

package singleton.pattern.demo5;

/**
 * 演示 懶漢式單例模式,線程不一定安全,同步代碼塊版
 *
 */
public class Demo {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleTon();
        Singleton singleton2 = Singleton.getSingleTon();
        System.out.println(singleton1 == singleton2); // 由于獲取的為同一個對象,所以輸出為true
    }
}

/**
 * 懶漢式單例模式,線程不一定安全,同步代碼塊版
 */
class Singleton {
    // 在類的內(nèi)部聲明一個實例對象
    private static Singleton singleton;

    /**
     * 構(gòu)造器私有化(防止通過new創(chuàng)建實例對象)
     */
    private Singleton() {
    }

    /**
     * 向外暴露一個靜態(tài)的公共方法用于獲取實例對象,在方法內(nèi)部加個同步代碼塊。 當調(diào)用該方法時,才去檢查并創(chuàng)建一個實例對象。
     * 
     * @return 實例對象
     */
    public static Singleton getSingleTon() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}

(3)優(yōu)缺點:
  優(yōu)點:優(yōu)化了了上例 懶漢式單例模式(線程安全,同步方法) 的效率問題。
  缺點:能滿足大部分線程安全的情況,但是若線程A與線程B并發(fā)執(zhí)行到 if (singleton == null),此時同步代碼塊的作用就不存在了, 會產(chǎn)生多個實例對象。

 

8、雙重檢查(Double Check)

(1)步驟:
  step1:構(gòu)造器私有化(防止通過new創(chuàng)建實例對象)
  step2:在類的內(nèi)部聲明實例對象,并使用volatile關(guān)鍵字(保證可見性,且防止因JVM指令重排使代碼執(zhí)行順序不對,從而導(dǎo)致代碼執(zhí)行有誤)。
  step3:向外暴露一個靜態(tài)的公共方法(在靜態(tài)方法內(nèi)部定義一個代碼塊,解決同步問題)用于獲取實例對象,在調(diào)用該方法時,才去創(chuàng)建實例對象。在上例 懶漢式單例模式(同步代碼塊方法,線程不一定安全) 的基礎(chǔ)上,給同步代碼塊內(nèi)部加個檢查處理。
(2)代碼實現(xiàn):

package singleton.pattern.demo6;

/**
 * 演示 單例模式,雙重檢查版
 *
 */
public class Demo {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleTon();
        Singleton singleton2 = Singleton.getSingleTon();
        System.out.println(singleton1 == singleton2); // 由于獲取的為同一個對象,所以輸出為true
    }
}

/**
 * 單例模式,雙重檢查版
 */
class Singleton {
    // 在類的內(nèi)部聲明一個實例對象
    private static volatile Singleton singleton;

    /**
     * 構(gòu)造器私有化(防止通過new創(chuàng)建實例對象)
     */
    private Singleton() {
    }

    /**
     * 向外暴露一個靜態(tài)的公共方法用于獲取實例對象,在方法內(nèi)部加個同步代碼塊,在代碼塊內(nèi)部增加一個檢查處理。 當調(diào)用該方法時,才去檢查并創(chuàng)建一個實例對象。
     * 
     * @return 實例對象
     */
    public static Singleton getSingleTon() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

(3)優(yōu)缺點:
  優(yōu)點:線程安全,延遲加載,效率高。常用于多線程開發(fā)。

 

9、靜態(tài)內(nèi)部類

(1)步驟:
  step1:構(gòu)造器私有化(防止通過new創(chuàng)建實例對象)
  step2:在類的內(nèi)部定義一個靜態(tài)內(nèi)部類(只有被調(diào)用時,才會被加載),并在內(nèi)部類中實例化對象。
  step3:向外暴露一個靜態(tài)的公共方法,并在方法中調(diào)用靜態(tài)內(nèi)部類,用于獲取實例對象。
(2)代碼實現(xiàn):

package singleton.pattern.demo7;

/**
 * 演示 單例模式,靜態(tài)內(nèi)部類版
 *
 */
public class Demo {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleTon();
        Singleton singleton2 = Singleton.getSingleTon();
        System.out.println(singleton1 == singleton2); // 由于獲取的為同一個對象,所以輸出為true
    }
}

/**
 * 單例模式,靜態(tài)內(nèi)部類版
 */
class Singleton {
    /**
     * 構(gòu)造器私有化(防止通過new創(chuàng)建實例對象)
     */
    private Singleton() {
    }

    /**
     * 靜態(tài)內(nèi)部類,在被調(diào)用的時候才會被加載,實現(xiàn)懶加載。 且內(nèi)部使用靜態(tài)常量實例化一個對象,保證了線程安全問題。
     */
    public static class SingleTonInstance {
        public static final Singleton INSTANCE = new Singleton();
    }

    /**
     * 向外暴露一個靜態(tài)的公共方法用于獲取實例對象,在方法內(nèi)部加個同步代碼塊,在代碼塊內(nèi)部增加一個檢查處理。 當調(diào)用該方法時,才去檢查并創(chuàng)建一個實例對象。
     * 
     * @return 實例對象
     */
    public static Singleton getSingleTon() {
        return SingleTonInstance.INSTANCE; // 調(diào)用靜態(tài)內(nèi)部類的靜態(tài)屬性
    }
}

(3)優(yōu)缺點:
  優(yōu)點:利用JVM的類加載機制,保證了實例化對象時只有一個線程,從而線程安全。在被調(diào)用時靜態(tài)內(nèi)部類才會被加載并實例化對象,從而實現(xiàn)懶加載,效率高。

(4)UML圖:

 

10、枚舉

(1)步驟:
  step1:定義一個枚舉類型。
  step2:調(diào)用即可
(2)代碼實現(xiàn):

package singleton.pattern;

/**
 * 演示 單例模式,枚舉版
 *
 */
public class Demo {
    public static void main(String[] args) {
        SingleTon singleton1 = SingleTon.INSTACNE;
        SingleTon singleton2 = SingleTon.INSTACNE;
        System.out.println(singleton1 == singleton2); // 由于獲取的為同一個對象,所以輸出為true
    }
}

/**
 * 單例模式,枚舉版
 */
enum SingleTon {
    INSTACNE;
    public void show() {
        System.out.println("hello world");
    }
}

(3)優(yōu)缺點:
  優(yōu)點:使用枚舉類型創(chuàng)建(enum本質(zhì)是個繼承java.lang.Enum類的final class),保證線程安全,且可以防止反序列化重新創(chuàng)建新的對象。

 

11、JDK中的單例模式舉例(Runtime)

(1)部分源碼

public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {} 
}

(2)可以看到上述代碼中采用的是 餓漢式單例模式(靜態(tài)變量版)。

12、單例模式使用注意

(1)當頻繁創(chuàng)建、銷毀某個對象時,可以采用單例模式。
(2)使用單例模式時,需調(diào)用相關(guān)方法獲取實例,而不是通過new。
(3)當創(chuàng)建對象消耗資源過多時,但又經(jīng)常使用時,可以采用單例模式創(chuàng)建。

 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多