單例模式

單例模式


簡(jiǎn)介

一個(gè)類(lèi)有且僅有一個(gè)實(shí)例,并且自行實(shí)例化向整個(gè)系統(tǒng)提供

基本用法

Kotlin 兩種:不帶參、帶參幅虑。
Java 六種:懶漢劳澄、餓漢童本、雙重校驗(yàn)鎖摄职、靜態(tài)內(nèi)部類(lèi)舔涎、枚舉和集合管理镜会。

Kotlin 不帶參

Kotlin 中使用 object 來(lái)創(chuàng)建單例,不允許有任何構(gòu)造函數(shù)终抽,可在 init 代碼塊中初始化

fun main() {
    println(SingletonNormal == SingletonNormal) // init normal -> true
    SingletonNormal.action() // action
}

object SingletonNormal {
    init {
        println("init normal")
    }
}

Kotlin 帶參

fun main() {
    println(Manager.getInstance("hello") == Manager.getInstance("singleton")) // init hello -> true
    Manager.getInstance("singleton").action() // action
}

class Manager private constructor(name: String) {
    init {
        println("init $name")
    }

    companion object : SingletonHolder<Manager, String>(::Manager)

    fun action() {
        println("action")
    }
}

open class SingletonHolder<out T, in A>(creator: (A) -> T) {
    private var creator: ((A) -> T)? = creator

    @Volatile
    private var instance: T? = null

    fun getInstance(arg: A): T {
        val i = instance
        if (i != null) {
            return i
        }

        return synchronized(this) {
            val i2 = instance
            if (i2 != null) {
                i2
            } else {
                val created = creator!!(arg)
                instance = created
                creator = null
                created
            }
        }
    }
}

餓漢式

加載時(shí)實(shí)例化

public class SingletonHungry {

    private static SingletonHungry instance = new SingletonHungry();

    private SingletonHungry() {
    }

    public static SingletonHungry getInstance() {
        return instance;
    }
}

懶漢式

使用時(shí)實(shí)例化

public class SingletonLazy {

    private static SingletonLazy instance;

    private SingletonLazy() {
    }

    public static synchronized SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
}

雙重校驗(yàn)鎖

volatile修飾的變量的值戳表,將不會(huì)被本地線程緩存,所有對(duì)該變量的讀寫(xiě)都是直接操作共享內(nèi)存昼伴,從而確保多個(gè)線程能正確的處理該變量匾旭。

由于volatile關(guān)鍵字可能會(huì)屏蔽掉虛擬機(jī)中的一些必要的代碼優(yōu)化,所以運(yùn)行效率并不是很高圃郊。

public class SingletonLock {

    private volatile static SingletonLock instance; // 關(guān)鍵字volatile

    private SingletonLock() {
    }

    public static SingletonLock getInstance() {
        if (instance == null) {  // 先檢查實(shí)例是否存在
            synchronized (SingletonLock.class) {  // 同步塊价涝,線程安全的創(chuàng)建實(shí)例
                if (instance == null) {  // 再次檢查實(shí)例是否存在
                    instance = new SingletonLock();
                }
            }
        }
        return instance;
    }
}

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

推薦使用

public class SingletonHolder {

    private SingletonHolder() {
    }

    public static SingletonHolder getInstance() {
        return InstanceHolder.instance;
    }

    private static class InstanceHolder {
        private static final SingletonHolder instance = new SingletonHolder();
    }
}

枚舉

不僅能避免多線程同步問(wèn)題,而且還能防止反序列化重新創(chuàng)建新的對(duì)象持舆。1.5中才加入enum特性

public enum SingletonEnum {

    INSTANCE;

    public void otherMethod() {
        Log.d("SingletonEnum", String.valueOf(INSTANCE));
    }
}

集合管理

ConcurrentHashMap 線程安全Map色瘩,解決并發(fā)問(wèn)題

public class SingletonManager {

    private static ConcurrentHashMap<String, Object> singletonMap = new ConcurrentHashMap<>();

    private SingletonManager() {
    }

    public static void putInstance(String key, Object instance) {
        singletonMap.putIfAbsent(key, instance);
    }

    public static Object getInstance(String key) {
        return singletonMap.get(key);
    }
}

問(wèn)題

類(lèi)加載器

如果單例由不同的類(lèi)加載器載入,那便有可能存在多個(gè)單例類(lèi)的實(shí)例逸寓。假定不是遠(yuǎn)端存取居兆,例如一些servlet容器對(duì)每個(gè)servlet使用完全不同的類(lèi)加載器,這樣的話如果有兩個(gè)servlet訪問(wèn)一個(gè)單例類(lèi)竹伸,它們就都會(huì)有各自的實(shí)例泥栖。

解決方法:

private static Class getClass(String classname) throws ClassNotFoundException {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    if(classLoader == null){
        classLoader = Singleton.class.getClassLoader();
    }
    return classLoader.loadClass(classname);
}

序列化

如果Singleton實(shí)現(xiàn)了java.io.Serializable接口,那么這個(gè)類(lèi)的實(shí)例就可能被序列化和反序列化勋篓。不管怎樣吧享,如果你序列化一個(gè)單例類(lèi)的對(duì)象,接下來(lái)反序列化多個(gè)那個(gè)對(duì)象譬嚣,那你就會(huì)有多個(gè)單例類(lèi)的實(shí)例钢颂。

解決方法:

public class SingletonSerialize implements java.io.Serializable{

    private static SingletonSerialize instance = new SingletonSerialize();

    private SingletonSerialize() {
    }

    public static SingleSingletonSerializetonF getInstance() {
        return instance;
    }

    // 添加此方法確保反序列化單例
    private Object readResolve() throws java.io.ObjectStreamException {
        return instance;
    }
}

參考

[譯]Object的局限性——Kotlin中的帶參單例模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拜银,隨后出現(xiàn)的幾起案子殊鞭,更是在濱河造成了極大的恐慌,老刑警劉巖盐股,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钱豁,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡疯汁,警方通過(guò)查閱死者的電腦和手機(jī)牲尺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人谤碳,你說(shuō)我怎么就攤上這事溃卡。” “怎么了蜒简?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵匈仗,是天一觀的道長(zhǎng)锻弓。 經(jīng)常有香客問(wèn)我踊兜,道長(zhǎng)试浙,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任卷仑,我火速辦了婚禮峻村,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘锡凝。我一直安慰自己粘昨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布窜锯。 她就那樣靜靜地躺著张肾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪锚扎。 梳的紋絲不亂的頭發(fā)上吞瞪,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音工秩,去河邊找鬼尸饺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛助币,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播螟碎,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼眉菱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了掉分?” 一聲冷哼從身側(cè)響起俭缓,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎酥郭,沒(méi)想到半個(gè)月后华坦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡不从,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年惜姐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡歹袁,死狀恐怖坷衍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情条舔,我是刑警寧澤枫耳,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站孟抗,受9級(jí)特大地震影響迁杨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜凄硼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一仑最、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧帆喇,春花似錦警医、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至婉刀,卻和暖如春吟温,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背突颊。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工鲁豪, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人律秃。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓爬橡,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親棒动。 傳聞我的和親對(duì)象是個(gè)殘疾皇子糙申,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容