作者已經(jīng)搬遷去隔壁網(wǎng)站鲜结,也歡迎大家關(guān)注我們的寫作團(tuán)隊:天星技術(shù)團(tuán)隊媒至。
題外話
上一次被人說文章名字取得不霸氣今膊,于是這一次我采用了這么霸氣的名字崎苗,但實際上我是一個很低調(diào)的人狐粱。設(shè)計模式剛?cè)腴T的小伙伴可以先看看這篇《設(shè)計模式入門》,在文章末尾也將列出“設(shè)計模式系列”文章胆数。歡迎大家關(guān)注留言投幣丟香蕉肌蜻。
什么是單例模式
- 單例模式是設(shè)計模式中最簡單的形式之一。
- 一個類有且僅有一個對象實例必尼,并自行實例化向整個系統(tǒng)提供蒋搜。
為何要學(xué)習(xí)單例模式
1. 有些對象我們只需要一個。例如:線程池,緩存齿诞,對話框等等酸休,創(chuàng)建太多此類實例可能會導(dǎo)致程序行為異常、資源使用過量等問題祷杈。
2. 防止造成資源浪費(fèi)。靜態(tài)變量也可以給整個系統(tǒng)提供一個實例渗饮,但此對象在程序一開始就被創(chuàng)建好了但汞,萬一在某次運(yùn)行中沒有使用到此對象,資源就被浪費(fèi)了互站。
3. 沒有富蘿莉包養(yǎng)我私蕾。
走進(jìn)單例模式
單例模式有三個要點,分別是:
1. 僅有一個實例胡桃。
2. 自動實例化踩叭。
3. 向整個系統(tǒng)提供。
這也就是我們在寫單例模式時候必須要遵守的幾點翠胰。根據(jù)上面的條件容贝,我們能夠得出:
1. 構(gòu)造方法必須私有化,禁止外部隨意new Singleton()之景;
2. 類內(nèi)部實例化一個實例對象斤富。
3. 對外提供一個可以獲取內(nèi)部唯一實例對象的方法。
懶漢式
懶漢式這名字的精髓在于懶锻狗,在代碼中满力,這種懶代表著需要的時候才創(chuàng)建實例,不需要就不創(chuàng)建了轻纪,這樣保證了資源的不浪費(fèi)油额。
Java懶漢式
public class JavaSingleton {
private JavaSingleton(){}//私有構(gòu)造方法
private static JavaSingleton instance;//提供一個實例對象
public static JavaSingleton getInstance(){//對外提供可獲取唯一實例化對象的方法
if(instance == null)
instance = new JavaSingleton(); //延遲實例化
return instance;
}
}
Kotlin懶漢式1
class KotlinSingleton private constructor() {
private var instance : KotlinSingleton? = null
fun getInstance(): KotlinSingleton? {
if(instance == null)
instance = KotlinSingleton()
return instance
}
}
Kotlin懶漢式2
class KotlinSingleton private constructor() {
companion object {
val instance by lazy(LazyThreadSafetyMode.NONE) {
KotlinSingleton()
}
}
}
// 在Kotlin 中調(diào)用
KotlinSingleton.instance.xx()
// 在Java 中調(diào)用
KotlinSingleton.Companion.getInstance().xx()
kotlin方式1就是直譯了java方式,沒啥說的刻帚。kotlin方式2寫著很簡潔潦嘶,也很明了。要提一點的就是companion object我擂,他類似public static衬以,這一點從下面調(diào)用方式也看得出來。lazy屬性表明是懶加載方式校摩,LazyThreadSafetyMode.NONE表明了這種方式看峻,線程不安全。
為啥不安全衙吩?互妓?!!
我們已經(jīng)按照單例模式三個要點寫出了單例模式冯勉,其實都已經(jīng)講完了澈蚌。但為何還有其他一些寫法呢? 因為我們是程序員白普宛瞄!優(yōu)化!優(yōu)化交胚!優(yōu)化份汗!
單線程進(jìn)程下,懶漢式完全夠用蝴簇。但實際開發(fā)哪還有什么單線程程序杯活。既然存在多線程,就存在并發(fā)性熬词,如果兩個線程同時進(jìn)入非空判斷旁钧,問題就出現(xiàn)了。線程一可能創(chuàng)建了一個instance互拾,線程二因為已經(jīng)非空判斷為空了歪今,所以也創(chuàng)建了個instance。這樣程序肯定不會朝著你預(yù)想的方向去摩幔。所以我們可以給非空判斷加一把鎖彤委,防止多線程同時進(jìn)入非空判斷。
同步鎖
Java方式
public class JavaSingleton {
private JavaSingleton(){}//私有構(gòu)造方法
private static JavaSingleton instance;//提供一個實例對象
public static synchronized JavaSingleton getInstance(){//對外提供可獲取唯一實例化對象的方法
if(instance == null)
instance = new JavaSingleton(); //延遲實例化
return instance;
}
}
Kotlin方式
class KotlinSingleton private constructor() {
private var instance : KotlinSingleton? = null
@Synchronized //用注解就okk了或衡。
fun getInstance(): KotlinSingleton? {
if(instance == null)
instance = KotlinSingleton()
return instance
}
}
簡單焦影!粗暴!
太粗暴了封断!Synchronized 是一把重型鎖斯辰,對性能影響相當(dāng)?shù)拇螅駟卫@樣在軟件中可能多次獲取坡疼,那性能將會大大地降低彬呻!所以,這種方式不建議使用柄瑰。
餓漢式
為了解決多線程的問題闸氮,又不想降低性能,“餓漢式”就來了教沾。餓漢式名字精髓在于“餓”蒲跨,迫切的想吃東西,在代碼中表示授翻,迫切的想有一個單例對象或悲!即在初始化的時候孙咪,就把單例創(chuàng)建好,之后要用就直接return巡语,但是不用就浪費(fèi)了翎蹈。這也是餓漢式的缺點。
Java方式
public class JavaSingleton {
private JavaSingleton(){}//私有構(gòu)造方法
private static JavaSingleton instance = new JavaSingleton();//提供一個實例對象
public static JavaSingleton getInstance(){//對外提供可獲取唯一實例化對象的方法
return instance;
}
}
Kotlin方式
object KotlinSingleton {
//null
}
// 在Kotlin 中調(diào)用
KotlinSingleton.xx()
// 在Java 中調(diào)用
KotlinSingleton.INSTANCE.xx()
酥糊男公!Kotlin荤堪!有牌面!
雙重檢查鎖
前面講的三種方式都有不同程度的缺點枢赔。而雙重檢查鎖逞力,既保證了延遲加載不浪費(fèi)資源,又保證了較好的性能糠爬,不采用重型鎖。但雙重檢查鎖不適用于1.4以及更早版本的java举庶。
Java方式
public class JavaSingleton {
private JavaSingleton(){}
//volatile確保了當(dāng)instance初始化為JavaSingleton實例時执隧,多個線程正確的處理instance變量。
private volatile static JavaSingleton instance;
public static JavaSingleton getInstance(){
if(instance == null){
synchronized (JavaSingleton.class){//只有第一次才徹底執(zhí)行鎖塊代碼
if(instance == null){
instance = new JavaSingleton();
}
}
}
return instance;
}
}
Kotlin方式1
class KotlinSingleton private constructor() {
@Volatile //用注解就okk了
private var instance : KotlinSingleton? = null
fun getInstance(): KotlinSingleton? {
if(instance == null){
synchronized(KotlinSingleton::class){
if(instance == null){
instance = KotlinSingleton()
}
}
}
return instance
}
}
Kotlin方式2
class KotlinSingleton private constructor() {
companion object {
val instance by lazy (LazyThreadSafetyMode.SYNCHRONIZED){
KotlinSingleton()
}
}
}
// 在Kotlin 中調(diào)用
KotlinSingleton.instance.xx()
// 在Java 中調(diào)用
KotlinSingleton.Companion.getInstance().xx()
“雙重檢查鎖”的沒有明顯的缺點户侥,如果非要說一個镀琉,可能就是太復(fù)雜了。kotlin還好蕊唐,java里面寫著相當(dāng)?shù)膹?fù)雜屋摔。如果程序?qū)π阅軟]有考慮的話,這樣寫顯然就太麻煩了替梨。
關(guān)于LazyThreadSafetyMode
延遲屬性 Lazy 可選LazyThreadSafetyMode三種模式:
- SYNCHRONIZED —— 雙重檢查鎖式钓试,默認(rèn)使用。
- PUBLICATION —— 允許多個線程同時初始化實例副瀑,但只采用最先返回的實例弓熏。
- NONE —— 沒有任何的線程安全的保證和開銷。
內(nèi)部類式(店長推薦?匪M炀稀)
不要私信問我店長是誰!
總之這種方式狈孔,解決了延遲加載信认,線程安全的問題,還代碼量少均抽!簡直美滋滋嫁赏!但跟之前不同的是,沒有聲明實例對象到忽。
Java方式
public class JavaSingleton {
private JavaSingleton(){}
private static class Holder{
private static JavaSingleton instance = new JavaSingleton();
}
public static JavaSingleton getInstance(){
return Holder.instance;
}
}
Kotlin方式
class KotlinSingleton private constructor() {
companion object {
fun getInstance() = Holder.instance
}
private object Holder{
val instance = KotlinSingleton()
}
}
在類加載時橄教,因為沒有調(diào)用getInstance()所以Holder也不會加載清寇,這樣就實現(xiàn)了懶加載。調(diào)用getInstance()時护蝶,JVM會主動保證類加載的安全性华烟,所以線程也是安全的。kotlin的寫法就是java的翻譯版本持灰。
民間大神版本
上面幾種方式都是比較官方的版本盔夜,下面介紹幾個民間版本,是真大神還是抖機(jī)靈堤魁,自行判斷喂链。我也去研究研究!
來自知乎@丌冰:
“我覺得這樣就好了妥泉,什么懶加載椭微,雙重什么什么,什么什么的”
fun main(args: Array<String>) {
Instance.INSTANCE.fun1()
print(Instance.INSTANCE.fun2())
}
enum class Instance {
INSTANCE;
fun fun1(){
}
fun fun2():Any?{
return null
}
}
作者:丌冰
鏈接:https://www.zhihu.com/question/52377186/answer/303561470
來源:知乎
著作權(quán)歸作者所有盲链。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)蝇率,非商業(yè)轉(zhuǎn)載請注明出處。
可見Android官方并不推薦使用枚舉刽沾,占用內(nèi)存較多本慕。
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<String,Object>();
private Singleton() {
}
public static void registerService(String key, Objectinstance) {
if (!objMap.containsKey(key) ) {
objMap.put(key, instance) ;
}
}
public static ObjectgetService(String key) {
return objMap.get(key) ;
}
}
這也是比較少見的單例寫法,將多個單例放在進(jìn)SingletonManager 的靜態(tài)map中統(tǒng)一管理侧漓。我是覺得有點太過于復(fù)雜锅尘,容易出錯。
以上版本僅供參考布蔗,要是用了對程序造成了什么不好的影響藤违,別找我。
關(guān)于單例的其他問題
關(guān)于繼承: 不能繼承何鸡!別想了纺弊。
關(guān)于單例: 盡量少使用單例。
關(guān)于我還沒想到的問題: 歡迎加群討論557247785骡男。
總結(jié)
單例方式 | 優(yōu)點 | 缺點 |
---|---|---|
懶漢式 | 懶加載 | 線程不安全 |
同步鎖 | 線程安全 | 多次獲取的性能很低 |
餓漢式 | 簡單淆游、易寫 | 可能引起資源浪費(fèi) |
雙重檢查鎖 | 第一次獲取才加鎖 | 寫法復(fù)雜 |
內(nèi)部類式 | 簡單、易寫 | 暫無 |
民間大神式 | 歡迎留言跟我討論 | 或者加qq群:557247785 |
以下是我“設(shè)計模式系列”文章隔盛,歡迎大家關(guān)注留言投幣丟香蕉犹菱。
也可以進(jìn)群跟大神們討論。qq群:557247785
設(shè)計模式入門
Java與Kotlin的單例模式
Kotlin的裝飾者模式與源碼擴(kuò)展
由淺到深了解工廠模式
為了學(xué)習(xí)Rxjava吮炕,年輕小伙竟作出這種事腊脱!