Java單例模式的五種實現(xiàn)方式
前言
單例模式(Singleton Pattern)是 Java 中最簡單的設(shè)計模式之一。這種類型的設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。
這種模式涉及到一個單一的類,該類負責(zé)創(chuàng)建自己的對象,同時確保只有單個對象被創(chuàng)建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
餓漢單例
是否多線程安全:是
是否懶加載:否
正如名字含義,餓漢需要直接創(chuàng)建實例。
public class EhSingleton { private static EhSingleton ehSingleton = new EhSingleton(); private EhSingleton() {} public static EhSingleton getInstance(){ return ehSingleton; } }
缺點: 類加載就初始化,浪費內(nèi)存
優(yōu)點: 沒有加鎖,執(zhí)行效率高。還是線程安全的實例。
懶漢單例
懶漢單例,在類初始化不會創(chuàng)建實例,只有被調(diào)用時才會創(chuàng)建實例。
非線程安全的懶漢單例
是否多線程安全:否
是否懶加載: 是
public class LazySingleton { private static LazySingleton ehSingleton; private LazySingleton() {} public static LazySingleton getInstance() { if (ehSingleton == null) { ehSingleton = new LazySingleton(); } return ehSingleton; } }
實例在調(diào)用 getInstance
才會創(chuàng)建實例,這樣的優(yōu)點是不占內(nèi)存,在單線程模式下,是安全的。但是多線程模式下,多個線程同時執(zhí)行 if (ehSingleton == null)
結(jié)果都為 true
,會創(chuàng)建多個實例,所以上面的懶漢單例是一個線程不安全的實例。
加同步鎖的懶漢單例
是否多線程安全:是
是否懶加載: 是
為了解決多個線程同時執(zhí)行 if (ehSingleton == null)
的問題,getInstance
方法添加同步鎖,這樣就保證了一個線程進入了 getInstance
方法,別的線程就無法進入該方法,只有執(zhí)行完畢之后,其他線程才能進入該方法,同一時間只有一個線程才能進入該方法。
public class LazySingletonSync { private static LazySingletonSync lazySingletonSync; private LazySingletonSync() {} public static synchronized LazySingletonSync getInstance() { if (lazySingletonSync == null) { lazySingletonSync =new LazySingletonSync(); } return lazySingletonSync; } }
這樣配置雖然保證了線程的安全性,但是效率低,只有在第一次調(diào)用初始化之后,才需要同步,初始化之后都不需要進行同步。鎖的粒度太大,影響了程序的執(zhí)行效率。
雙重檢驗懶漢單例
是否多線程安全:是
是否懶加載:是
使用 synchronized
聲明的方法,在多個線程訪問,比如A線程訪問時,其他線程必須等待A線程執(zhí)行完畢之后才能訪問,大大的降低的程序的運行效率。這個時候使用 synchronized
代碼塊優(yōu)化執(zhí)行時間,減少鎖的粒度。
雙重檢驗首先判斷實例是否為空,然后使用 synchronized (LazySingletonDoubleCheck.class)
使用類鎖,鎖住整個類,執(zhí)行完代碼塊的代碼之后,新建了實例,其他代碼都不走 if (lazySingletonDoubleCheck == null)
里面,只會在最開始的時候效率變慢。而 synchronized
里面還需要判斷是因為可能同時有多個線程都執(zhí)行到 synchronized (LazySingletonDoubleCheck.class)
,如果有一個線程線程新建實例,其他線程就能獲取到 lazySingletonDoubleCheck
不為空,就不會再創(chuàng)建實例了。
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)部類
是否多線程安全:是
是否懶加載:是
外部類加載時,并不會加載內(nèi)部類,也就不會執(zhí)行 new SingletonHolder()
,這屬于懶加載。只有第一次調(diào)用 getInstance()
方法時才會加載 SingletonHolder
類。而靜態(tài)內(nèi)部類是線程安全的。
靜態(tài)內(nèi)部類為什么是線程安全
靜態(tài)內(nèi)部類利用了類加載機制的初始化階段 方法,靜態(tài)內(nèi)部類的靜態(tài)變量賦值操作,實際就是一個 方法,當執(zhí)行 getInstance()
方法時,虛擬機才會加載 SingletonHolder
靜態(tài)內(nèi)部類,
然后在加載靜態(tài)內(nèi)部類,該內(nèi)部類有靜態(tài)變量,JVM會改內(nèi)部生成方法,然后在初始化執(zhí)行方法 —— 即執(zhí)行靜態(tài)變量的賦值動作。
虛擬機會保證 方法在多線程環(huán)境下使用加鎖同步,只會執(zhí)行一次 方法。
這種方式不僅實現(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é)
- 餓漢單例類加載就初始化,在沒有加鎖的情況下實現(xiàn)了線程安全,執(zhí)行效率高。但是無論有沒有調(diào)用實例都會被創(chuàng)建,比較浪費內(nèi)存。
- 為了解決內(nèi)存的浪費,使用了懶漢單例,但是懶漢單例在多線程下會引發(fā)線程不安全的問題。
- 不安全的懶漢單例,使用
synchronized
聲明同步方法,獲取實例就是安全了。 synchronized
聲明方法每次線程調(diào)用方法,其它線程只能等待,降低了程序的運行效率。- 為了減少鎖的粒度,使用
synchronized
代碼塊,因為只有少量的線程獲取實例,實例是null,創(chuàng)建實例之后,后續(xù)的線程都能獲取到線程,也就無需使用鎖了??赡芏鄠€線程執(zhí)行到synchronized
,所以同步代碼塊還需要再次判斷一次。 - 靜態(tài)內(nèi)部類賦值實際是調(diào)用 方法,而虛擬機保證 方法使用鎖,保證線程安全。
到此這篇關(guān)于Java單例模式的五種實現(xiàn)方式的文章就介紹到這了,更多相關(guān)Java單例模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在Windows系統(tǒng)下安裝Thrift的方法與使用講解
今天小編就為大家分享一篇關(guān)于在Windows系統(tǒng)下安裝Thrift的方法與使用講解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12spring使用RedisTemplate的操作類訪問Redis
本篇文章主要介紹了spring使用RedisTemplate的操作類訪問Redis,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05FasfDFS整合Java實現(xiàn)文件上傳下載功能實例詳解
這篇文章主要介紹了FasfDFS整合Java實現(xiàn)文件上傳下載功能實例詳解,需要的朋友可以參考下2017-08-08Jmeter?BlazeMeter實現(xiàn)web錄制過程
BlazeMeter是一款與Apache JMeter兼容的chrome插件,采用BlazeMeter可以方便的進行流量錄制和腳本生成,作為接口測試腳本編寫的一個基礎(chǔ),這篇文章主要介紹了Jmeter?BlazeMeter實現(xiàn)web錄制,需要的朋友可以參考下2021-12-12SpringBoot整合RocketMQ實現(xiàn)消息發(fā)送和接收的詳細步驟
這篇文章主要介紹了SpringBoot整合RocketMQ實現(xiàn)消息發(fā)送和接收功能,我們使用主流的SpringBoot框架整合RocketMQ來講解,使用方便快捷,本文分步驟給大家介紹的非常詳細,需要的朋友可以參考下2021-08-08