單例模式(Singleton)

單例模式:保證一個(gè)類只有一個(gè)實(shí)例,并且提供一個(gè)可以訪問的全局入口。

優(yōu)點(diǎn):節(jié)省內(nèi)存、節(jié)省計(jì)算、方便管理
應(yīng)用舉例:無狀態(tài)的工具類(日志工具、字符串工具)、全局信息類(全局記數(shù)、環(huán)境變量)
常見的五種寫法:從簡(jiǎn)單到難遞進(jìn)

餓漢式寫法:

優(yōu)點(diǎn):類裝載的時(shí)候就完成了初始化,避免了線程同步的問題。
缺點(diǎn):從始至終沒有使用實(shí)例,造成內(nèi)存浪費(fèi)。

/**
 * 餓漢式:
 */
public class Singleton {
    private static Singleton singleton = new Singleton();

    private Singleton() {

    }

    public static Singleton getInstance() {
        return singleton;
    }
}
靜態(tài)代碼塊:

優(yōu)點(diǎn):類裝載的時(shí)候就完成了初始化,避免了線程同步的問題。同(餓漢式)
缺點(diǎn):從始至終沒有使用實(shí)例,造成內(nèi)存浪費(fèi)。同(餓漢式)

/**
 * 靜態(tài)代碼塊
 */
public class Singleton {
    private static Singleton singleton;

    static {
        singleton = new Singleton();
    }

    private Singleton() {

    }

    public static Singleton getInstance() {
        return singleton;
    }
}
懶漢式:

優(yōu)點(diǎn):在getInstance被調(diào)用的時(shí)候,才實(shí)例化對(duì)象。
缺點(diǎn):只能在單線程下使用,多想成環(huán)境下,如果一個(gè)線程通過 if (singleton == null) {}還沒往下執(zhí)行,另一個(gè)線程也通過if (singleton == null) {}這時(shí)會(huì)多次創(chuàng)建實(shí)例。

/**
 * 懶漢式
 */
public class Singleton {
    private static Singleton singleton;

    private Singleton() {

    }

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}
線程安全懶漢式:

優(yōu)點(diǎn):解決了懶漢式線程安全問題。
缺點(diǎn):效率太低,多個(gè)線程不能同時(shí)訪問getInstance()方法,不能防止反序列化,生成多個(gè)實(shí)例。

/**
 * 線程安全懶漢式
 */
public class Singleton {
    private static Singleton singleton;

    private Singleton() {

    }

    public static synchronized Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}
雙重檢查模式:

優(yōu)點(diǎn):實(shí)例代碼只調(diào)用一次,后來判斷**if (singleton == null) 只需要判斷外層即可跳出,同時(shí)線程安全,延遲加載,效率更高。
缺點(diǎn):效率太低,多個(gè)線程不能同時(shí)訪問getInstance()方法。
1、為什么要判斷兩次為空?去掉第二層可以嗎?
答案:兩個(gè)線程同時(shí)調(diào)用getInstance方法,并且singleton為null,兩個(gè)線程都可以通過第一層為null判斷,由于鎖機(jī)制的存在,一個(gè)線程執(zhí)行完singleton = new Singleton()之后退出synchronized保護(hù)區(qū)域,如果沒有第二層if (singleton == null)判斷,第二個(gè)線程也會(huì)執(zhí)行singleton = new Singleton(),這樣就破壞了單例。
如果去掉第一個(gè) if (singleton == null)所有線程會(huì)串行執(zhí)行,效率低下

2、為什么要加volatile?
答案:singleton = new Singleton()并非原子操作,在JVM中至少做了三件事。
1)給singleton分配內(nèi)存空間
2)調(diào)用Singleton的構(gòu)造函數(shù)來初始化singleton
3)將singleton對(duì)象指向分配的內(nèi)存空間(執(zhí)行完singleton就不是null了)
存在重排序的優(yōu)化,可能執(zhí)行順序?yàn)?)3)2),當(dāng)執(zhí)行完3)后對(duì)象已經(jīng)實(shí)例化,但并未初始化,其他線程使用該對(duì)象就會(huì)報(bào)錯(cuò),其他線程使用報(bào)錯(cuò)后,對(duì)象才被初始化,可是已經(jīng)晚了。

** volatile可以禁止指令重排,使其按照1)2)3)順序執(zhí)行

/**
 * 雙重檢查模式
 */
public class Singleton {
    private static volatile Singleton singleton;

    private Singleton() {

    }

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

靜態(tài)內(nèi)部類的寫法:

優(yōu)點(diǎn):線程安全,需要實(shí)例時(shí)才時(shí)例化,延遲加載,效率高。
缺點(diǎn):不能防止反序列化,生成多個(gè)實(shí)例。

/**
 * 靜態(tài)內(nèi)部類
 */
public class Singleton {

    private Singleton() {

    }

    private static class SingletonInstance {
        private static final Singleton singleton = new Singleton();

    }

    public static Singleton getInstance() {
        return SingletonInstance.singleton;
    }
}
枚舉式寫法

優(yōu)點(diǎn):避免多線程同步,JVM保證線程安全(反編譯之后看到各個(gè)枚舉項(xiàng)目通過static來定義和初始化,類加載時(shí)被初始化),防止反序列化、反射,破壞單例。其中java還針對(duì)枚舉類序列化做了規(guī)定,僅僅講枚舉類的name屬性輸出到結(jié)果中,反序列化時(shí)通過java.lang.Enum的valueof根據(jù)名字查找對(duì)西那個(gè),而不是新建一個(gè)對(duì)象。如果反射枚舉類會(huì)拋出異常。

/**
 * 枚舉式
 */
public enum Singleton {
    INSTANCE;

    public void whateverMethod() {
        System.out.println("執(zhí)行了單例方法,例如返回環(huán)境變量信息");

    }

    public static void main(String[] args) {
        //使用枚舉寫法表示的單例類
        Singleton.INSTANCE.whateverMethod();
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容