Java單例模式的五種實(shí)現(xiàn)方式
前言
單例模式(Singleton Pattern)是 Java 中最簡(jiǎn)單的設(shè)計(jì)模式之一。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式。
這種模式涉及到一個(gè)單一的類,該類負(fù)責(zé)創(chuàng)建自己的對(duì)象,同時(shí)確保只有單個(gè)對(duì)象被創(chuàng)建。這個(gè)類提供了一種訪問其唯一的對(duì)象的方式,可以直接訪問,不需要實(shí)例化該類的對(duì)象。
餓漢單例
是否多線程安全:是
是否懶加載:否
正如名字含義,餓漢需要直接創(chuàng)建實(shí)例。
public class EhSingleton {
private static EhSingleton ehSingleton = new EhSingleton();
private EhSingleton() {}
public static EhSingleton getInstance(){
return ehSingleton;
}
}
缺點(diǎn): 類加載就初始化,浪費(fèi)內(nèi)存
優(yōu)點(diǎn): 沒有加鎖,執(zhí)行效率高。還是線程安全的實(shí)例。
懶漢單例
懶漢單例,在類初始化不會(huì)創(chuàng)建實(shí)例,只有被調(diào)用時(shí)才會(huì)創(chuàng)建實(shí)例。
非線程安全的懶漢單例
是否多線程安全:否
是否懶加載: 是
public class LazySingleton {
private static LazySingleton ehSingleton;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (ehSingleton == null) {
ehSingleton = new LazySingleton();
}
return ehSingleton;
}
}
實(shí)例在調(diào)用 getInstance 才會(huì)創(chuàng)建實(shí)例,這樣的優(yōu)點(diǎn)是不占內(nèi)存,在單線程模式下,是安全的。但是多線程模式下,多個(gè)線程同時(shí)執(zhí)行 if (ehSingleton == null) 結(jié)果都為 true,會(huì)創(chuàng)建多個(gè)實(shí)例,所以上面的懶漢單例是一個(gè)線程不安全的實(shí)例。
加同步鎖的懶漢單例
是否多線程安全:是
是否懶加載: 是
為了解決多個(gè)線程同時(shí)執(zhí)行 if (ehSingleton == null) 的問題,getInstance 方法添加同步鎖,這樣就保證了一個(gè)線程進(jìn)入了 getInstance 方法,別的線程就無法進(jìn)入該方法,只有執(zhí)行完畢之后,其他線程才能進(jìn)入該方法,同一時(shí)間只有一個(gè)線程才能進(jìn)入該方法。
public class LazySingletonSync {
private static LazySingletonSync lazySingletonSync;
private LazySingletonSync() {}
public static synchronized LazySingletonSync getInstance() {
if (lazySingletonSync == null) {
lazySingletonSync =new LazySingletonSync();
}
return lazySingletonSync;
}
}
這樣配置雖然保證了線程的安全性,但是效率低,只有在第一次調(diào)用初始化之后,才需要同步,初始化之后都不需要進(jìn)行同步。鎖的粒度太大,影響了程序的執(zhí)行效率。
雙重檢驗(yàn)懶漢單例
是否多線程安全:是
是否懶加載:是
使用 synchronized 聲明的方法,在多個(gè)線程訪問,比如A線程訪問時(shí),其他線程必須等待A線程執(zhí)行完畢之后才能訪問,大大的降低的程序的運(yùn)行效率。這個(gè)時(shí)候使用 synchronized 代碼塊優(yōu)化執(zhí)行時(shí)間,減少鎖的粒度。
雙重檢驗(yàn)首先判斷實(shí)例是否為空,然后使用 synchronized (LazySingletonDoubleCheck.class) 使用類鎖,鎖住整個(gè)類,執(zhí)行完代碼塊的代碼之后,新建了實(shí)例,其他代碼都不走 if (lazySingletonDoubleCheck == null) 里面,只會(huì)在最開始的時(shí)候效率變慢。而 synchronized 里面還需要判斷是因?yàn)榭赡芡瑫r(shí)有多個(gè)線程都執(zhí)行到 synchronized (LazySingletonDoubleCheck.class) ,如果有一個(gè)線程線程新建實(shí)例,其他線程就能獲取到 lazySingletonDoubleCheck 不為空,就不會(huì)再創(chuàng)建實(shí)例了。
public class LazySingletonDoubleCheck {
private static LazySingletonDoubleCheck lazySingletonDoubleCheck;
private LazySingletonDoubleCheck() {}
public static LazySingletonDoubleCheck getInstance() {
if (lazySingletonDoubleCheck == null) {
synchronized (LazySingletonDoubleCheck.class) {
if (lazySingletonDoubleCheck == null) {
lazySingletonDoubleCheck = new LazySingletonDoubleCheck();
}
}
}
return lazySingletonDoubleCheck;
}
}
靜態(tài)內(nèi)部類
是否多線程安全:是
是否懶加載:是
外部類加載時(shí),并不會(huì)加載內(nèi)部類,也就不會(huì)執(zhí)行 new SingletonHolder(),這屬于懶加載。只有第一次調(diào)用 getInstance() 方法時(shí)才會(huì)加載 SingletonHolder 類。而靜態(tài)內(nèi)部類是線程安全的。
靜態(tài)內(nèi)部類為什么是線程安全
靜態(tài)內(nèi)部類利用了類加載機(jī)制的初始化階段 方法,靜態(tài)內(nèi)部類的靜態(tài)變量賦值操作,實(shí)際就是一個(gè) 方法,當(dāng)執(zhí)行 getInstance() 方法時(shí),虛擬機(jī)才會(huì)加載 SingletonHolder 靜態(tài)內(nèi)部類,
然后在加載靜態(tài)內(nèi)部類,該內(nèi)部類有靜態(tài)變量,JVM會(huì)改內(nèi)部生成方法,然后在初始化執(zhí)行方法 —— 即執(zhí)行靜態(tài)變量的賦值動(dòng)作。
虛擬機(jī)會(huì)保證 方法在多線程環(huán)境下使用加鎖同步,只會(huì)執(zhí)行一次 方法。
這種方式不僅實(shí)現(xiàn)延遲加載,也保障線程安全。
public class StaticClass {
private StaticClass() {}
private static class SingletonHolder {
private static final SingletonHolder INSTANCE = new SingletonHolder();
}
public static final SingletonHolder getInstance() {
return SingletonHolder.INSTANCE;
}
}
總結(jié)
- 餓漢單例類加載就初始化,在沒有加鎖的情況下實(shí)現(xiàn)了線程安全,執(zhí)行效率高。但是無論有沒有調(diào)用實(shí)例都會(huì)被創(chuàng)建,比較浪費(fèi)內(nèi)存。
- 為了解決內(nèi)存的浪費(fèi),使用了懶漢單例,但是懶漢單例在多線程下會(huì)引發(fā)線程不安全的問題。
- 不安全的懶漢單例,使用
synchronized聲明同步方法,獲取實(shí)例就是安全了。 synchronized聲明方法每次線程調(diào)用方法,其它線程只能等待,降低了程序的運(yùn)行效率。- 為了減少鎖的粒度,使用
synchronized代碼塊,因?yàn)橹挥猩倭康木€程獲取實(shí)例,實(shí)例是null,創(chuàng)建實(shí)例之后,后續(xù)的線程都能獲取到線程,也就無需使用鎖了??赡芏鄠€(gè)線程執(zhí)行到synchronized,所以同步代碼塊還需要再次判斷一次。 - 靜態(tài)內(nèi)部類賦值實(shí)際是調(diào)用 方法,而虛擬機(jī)保證 方法使用鎖,保證線程安全。
到此這篇關(guān)于Java單例模式的五種實(shí)現(xiàn)方式的文章就介紹到這了,更多相關(guān)Java單例模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?將list集合數(shù)據(jù)按照時(shí)間字段排序的方法
這篇文章主要介紹了Java?將list集合數(shù)據(jù)按照時(shí)間字段排序,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03
SpringBoot實(shí)現(xiàn)文件在線預(yù)覽功能的全過程
我們開發(fā)業(yè)務(wù)系統(tǒng)的時(shí)候,經(jīng)常有那種文檔文件在線預(yù)覽的需求,下面這篇文章主要給大家介紹了關(guān)于SpringBoot實(shí)現(xiàn)文件在線預(yù)覽功能的相關(guān)資料,需要的朋友可以參考下2021-11-11
一篇文章帶你了解Maven的坐標(biāo)概念以及依賴管理
這篇文章主要為大家介紹了Maven的坐標(biāo)概念以及依賴管理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-01-01
詳解在springmvc中解決FastJson循環(huán)引用的問題
本篇文章主要介紹了在springmvc中解決FastJson循環(huán)引用的問題,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-01-01
Intellij IDEA 旗艦版創(chuàng)建 Spring MVC 項(xiàng)目踩過的坑
IDEA旗艦版可以直接創(chuàng)建Spring MVC項(xiàng)目,但創(chuàng)建后的項(xiàng)目并不是直接就可以運(yùn)行,還需要進(jìn)行一些配置。這篇文章主要介紹了Intellij IDEA 旗艦版創(chuàng)建 Spring MVC 項(xiàng)目踩坑記 ,需要的朋友可以參考下2020-03-03
詳解springcloud之服務(wù)注冊(cè)與發(fā)現(xiàn)
本次分享的是關(guān)于springcloud服務(wù)注冊(cè)與發(fā)現(xiàn)的內(nèi)容,將通過分別搭建服務(wù)中心,服務(wù)注冊(cè),服務(wù)發(fā)現(xiàn)來說明,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2018-06-06
詳解hibernate雙向多對(duì)多關(guān)聯(lián)映射XML與注解版
本篇文章主要介紹了詳解hibernate雙向多對(duì)多關(guān)聯(lián)映射XML與注解版,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05

