【设计模式】实现线程安全单例模式的五种方式
Python微信訂餐小程序課程視頻
https://edu.csdn.net/course/detail/36074
Python實(shí)戰(zhàn)量化交易理財(cái)系統(tǒng)
https://edu.csdn.net/course/detail/35475
餓漢式
餓漢式:類加載就會(huì)導(dǎo)致該單實(shí)例對(duì)象被創(chuàng)建
復(fù)制代碼- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
java`// 問(wèn)題1:為什么加 final
// 問(wèn)題2:如果實(shí)現(xiàn)了序列化接口, 還要做什么來(lái)防止反序列化破壞單例
public final class Singleton_hungry implements Serializable {
// 問(wèn)題3:為什么設(shè)置為私有? 是否能防止反射創(chuàng)建新的實(shí)例?
private Singleton_hungry(){}
// 問(wèn)題4:這樣初始化是否能保證單例對(duì)象創(chuàng)建時(shí)的線程安全?
private static Singleton_hungry INSTANCE = new Singleton_hungry();
// 問(wèn)題5:為什么提供靜態(tài)方法而不是直接將 INSTANCE 設(shè)置為 public, 說(shuō)出你知道的理由
public static Singleton_hungry getInstance() {
return INSTANCE;
}
public Object readResolve(){ // 防止反射創(chuàng)建新的實(shí)例?
return INSTANCE;
}
}`
- 問(wèn)題1:
避免子類覆蓋父類的一些方法,導(dǎo)致線程不安全。 - 問(wèn)題2:
實(shí)現(xiàn) readResolve 方法。當(dāng)從對(duì)象流 ObjectInputStream 中讀取對(duì)象時(shí),會(huì)檢查對(duì)象的類否定義了 readResolve 方法。如果定義了,則調(diào)用它返回我們想指定的對(duì)象(這里就指定了返回單例對(duì)象)。 - 問(wèn)題3:防止通過(guò) new 創(chuàng)建對(duì)象實(shí)例。不能防止反射創(chuàng)建新的實(shí)例。
- 問(wèn)題4:可以。靜態(tài)變量初始化在類加載時(shí)進(jìn)行,由 jvm 進(jìn)行管理,可以保證線程安全。
- 問(wèn)題5:通過(guò)方法,可以提高拓展性,改進(jìn)餓漢式轉(zhuǎn)化為懶漢式、利用泛型特性、增加對(duì)單例對(duì)象的控制操作。
枚舉單例
復(fù)制代碼- 1
- 2
- 3
javaenum Singleton { INSTANCE; }
- 問(wèn)題1:枚舉單例是如何限制實(shí)例個(gè)數(shù)的
單例相當(dāng)于枚舉的靜態(tài)成員變量,定義幾個(gè)就有幾個(gè)實(shí)例。 - 問(wèn)題2:枚舉單例在創(chuàng)建時(shí)是否有并發(fā)問(wèn)題
單例相當(dāng)于枚舉的靜態(tài)成員變量,類加載時(shí)初始化,由 jvm 進(jìn)行管理,可以保證線程安全。 - 問(wèn)題3:枚舉單例能否被反射破壞單例
不能 - 問(wèn)題4:枚舉單例能否被反序列化破壞單例
枚舉實(shí)現(xiàn)了 Serializable 接口,可序列化,但不會(huì)被反序列破壞單例。 - 問(wèn)題5:枚舉單例屬于懶漢式還是餓漢式
餓漢式 - 問(wèn)題6:枚舉單例如果希望加入一些單例創(chuàng)建時(shí)的初始化邏輯該如何做
枚舉允許構(gòu)造方法
懶漢式
復(fù)制代碼- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
typescriptpublic final class Singleton\_lazy { private Singleton\_lazy(){} private static Singleton_lazy INSTANCE = null; // 缺點(diǎn) public static synchronized Singleton_lazy getInstance() { if(INSTANCE != null) { return INSTANCE; } INSTANCE = new Singleton_lazy(); return INSTANCE; } }
- synchronized 保證線程安全,但鎖粒度較大,性能低。
DCL 懶漢式
復(fù)制代碼- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
java`public final class Singleton_DCL {
private Singleton_DCL() {}
// 問(wèn)題1:解釋為什么要加 volatile ?
private static volatile Singleton_DCL INSTANCE= null;
// 問(wèn)題2:對(duì)比實(shí)現(xiàn)3, 說(shuō)出這樣做的意義
public static Singleton_DCL getInstance() {
if(INSTANCE != null) {
return INSTANCE;
}
synchronized (Singleton_DCL.class) {
// 問(wèn)題3:為什么還要在這里加為空判斷, 之前不是判斷過(guò)了嗎
if(INSTANCE != null) {
return INSTANCE;
}
INSTANCE = new Singleton_DCL();
return INSTANCE;
}
}
}`
- 問(wèn)題1:避免指令重排序,導(dǎo)致賦值語(yǔ)句先于構(gòu)造函數(shù)執(zhí)行,得到一個(gè)未初始化完畢的對(duì)象。
- 問(wèn)題2、3:Double Check Lock 機(jī)制。同步代碼塊外部的判斷語(yǔ)句主要用于 INSTANCE 初始化并賦值之后,此時(shí) INSTANCE != null,如果有多個(gè)線程嘗試獲取單例,可以提前返回,不用執(zhí)行同步代碼塊。而同步代碼塊內(nèi)部的判斷主要用于第一次初始化時(shí),INSTANCE = null,此時(shí)可以有多個(gè)線程嘗試獲取 INSTANCE,只能有一個(gè)線程進(jìn)入同步代碼塊,其他線程在同步代碼塊外阻塞,該線程創(chuàng)建一個(gè)單例對(duì)象之后,喚醒其他線程,再進(jìn)入同步代碼塊,發(fā)現(xiàn) INSTANCE != null,則直接返回,不用重新創(chuàng)建單例對(duì)象,提高了效率。
靜態(tài)內(nèi)部類懶漢單例
復(fù)制代碼- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
java`public final class Singleton_LazyHolder {
private Singleton_LazyHolder(){}
// 問(wèn)題1:屬于懶漢式還是餓漢式
private static class LazyHolder{
static final Singleton_LazyHolder INSTANCE = new Singleton_LazyHolder();
}
// 問(wèn)題2:在創(chuàng)建時(shí)是否有并發(fā)問(wèn)題
public static Singleton_LazyHolder getInstance() {
return LazyHolder.INSTANCE;
}
}`
- 問(wèn)題1:懶漢式。靜態(tài)內(nèi)部類只有在被方法調(diào)用的時(shí)候才進(jìn)行初始化,類加載。
- 問(wèn)題2:無(wú),類加載由 jvm 進(jìn)行,線程安全。
總結(jié)
以上是生活随笔為你收集整理的【设计模式】实现线程安全单例模式的五种方式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 数字后端——物理单元介绍
- 下一篇: 数字后端——可制造性设计