9種Java單例模式詳解(推薦)
單例模式的特點(diǎn)
- 一個(gè)類只允許產(chǎn)生一個(gè)實(shí)例化對象。
- 單例類構(gòu)造方法私有化,不允許外部創(chuàng)建對象。
- 單例類向外提供靜態(tài)方法,調(diào)用方法返回內(nèi)部創(chuàng)建的實(shí)例化對象。
懶漢式(線程不安全)
其主要表現(xiàn)在單例類在外部需要?jiǎng)?chuàng)建實(shí)例化對象時(shí)再進(jìn)行實(shí)例化,進(jìn)而達(dá)到Lazy Loading 的效果。
通過靜態(tài)方法 getSingleton() 和private 權(quán)限構(gòu)造方法為創(chuàng)建一個(gè)實(shí)例化對象提供唯一的途徑。
不足:未考慮到多線程的情況下可能會存在多個(gè)訪問者同時(shí)訪問,發(fā)生構(gòu)造出多個(gè)對象的問題,所以在多線程下不可用這種方法。
/** * @author MrRoot * @since 2018-12-17 * 懶漢式(線程不安全) */ public class Singleton { private static Singleton singleton; private Singleton(){ } public static Singleton singleton(){ if (singleton == null){ singleton = new Singleton(); } return singleton; } }
懶漢式(線程安全,同步方法,不推薦使用)
針對懶漢式的線程不安全,自然會想到給 getSingleton() 進(jìn)行 synchronized 加鎖來保證線程同步。
不足:效率低。大多數(shù)情況下這個(gè)鎖占用的額外資源都浪費(fèi)了,每個(gè)線程在想獲得類的實(shí)例時(shí)候,執(zhí)行 getSingleton() 方法都要進(jìn)行同步。
/** * @author MrRoot * @since 2019-3-27 * 懶漢式(線程安全,同步方法,不推薦使用) */ public class Singleton { private static Singleton singleton; private Singleton(){ } public static synchronized Singleton singleton(){ if (singleton == null){ singleton = new Singleton(); } return singleton; } }
餓漢式(線程安全)
在進(jìn)行類加載時(shí)完成實(shí)例化對象的過程就是餓漢式的形式。
避免了線程同步問題,在運(yùn)行這個(gè)類的時(shí)候進(jìn)行加載,之后直接訪問
不足:相比接下來的靜態(tài)內(nèi)部類而言,這種方法比靜態(tài)內(nèi)部類多了內(nèi)存常駐,容易造成內(nèi)存浪費(fèi),也未達(dá)到延遲加載的效果。
/** * @author MrRoot * @since 2019-3-27 * 餓漢式(線程安全) */ public class Singleton{ private static Singleton singleton = new Singleton(); private Singleton(){ } public static Singleton singleton(){ return singleton; } }
靜態(tài)內(nèi)部類加載(線程安全)
靜態(tài)內(nèi)部類不會在單例加載時(shí)加載,當(dāng)調(diào)用 getSingleton() 方法時(shí)才會進(jìn)行加載,達(dá)到類似懶漢式效果,并且也是線程安全的。
類的靜態(tài)屬性只會在第一次加載類時(shí)進(jìn)行初始化,所以上面的方法JVM 幫助我們保證了線程的安全性,在類進(jìn)行初始化時(shí),其他線程無法進(jìn)入。
/** * @author MrRoot * @since 2019-3-27 * 靜態(tài)內(nèi)部類加載(線程安全) */ public class Singleton{ private static Singleton singleton; private static class SingletonInner{ private static final Singleton instance = new Singleton(); } public static Singleton getSingleton(){ return SingletonInner.instance; } }
枚舉(線程安全)
自由串行化;保證只有一個(gè)實(shí)例;線程安全。
Effective Java 作者所提倡的方法,近乎完美,在繼承場景下不適用。
/** * @author MrRoot * @since 2019-3-27 * 枚舉(線程安全) */ enum Singleton{ INSTANCE; public void method(){ } } class Test{ public static void main(String[] args) { Singleton.INSTANCE.method(); } }
懶漢式雙重校驗(yàn)鎖法(通常線程安全,不可保證完全安全)
使用同步代碼塊避免了第二種方法的效率低的問題,但此方法并不能完全起到線程同步的作用,與上面第一種方法產(chǎn)生的問題相似,多線程訪問時(shí)可能產(chǎn)生多個(gè)對象。
/** * @author MrRoot * @since 2019-3-27 * 懶漢式雙重校驗(yàn)鎖法(通常線程安全,不可保證完全安全) */ class Singleton{ private static Singleton singleton; private Singleton(){ } public static Singleton singleton(){ if (singleton == null){ synchronized (Singleton.class){ if (singleton == null){ singleton = new Singleton(); } } } return singleton; } }
懶漢式雙重檢查終極版
與第六種方法不同的是,此方法給singleton 的聲明上加了關(guān)鍵字 volatile ,進(jìn)而解決了低概率的線程不安全問題。
volatile 起到禁止指令重排的作用,在它賦值完成之前,就不會調(diào)用讀操作(singleton == null)。
/** * @author MrRoot * @since 2019-3-27 * 懶漢式雙重檢查終極版(volatile) */ class Singleton{ private static volatile Singleton singleton; private Singleton(){ } public static Singleton singleton(){ if (singleton == null){ synchronized (Singleton.class){ if (singleton == null){ singleton = new Singleton(); } } } return singleton; } }
使用 ThreadLocal 實(shí)現(xiàn)(線程安全)
ThreadLocal 會為每一個(gè)線程提供一個(gè)獨(dú)立的變量副本,從而隔離了多個(gè)線程對數(shù)據(jù)的訪問沖突。
對于多線程資源共享的問題,同步機(jī)制采用了“以時(shí)間換空間”的方式,而ThreadLocal 采用了“以空間換時(shí)間”的方式。前者僅提供一份變量,讓不同的線程排隊(duì)訪問,而后者為每一個(gè)線程都提供了一份變量,因此可以同時(shí)訪問而互不影響。
/** * @author MrRoot * @since 2019-3-27 * 使用 ThreadLocal 實(shí)現(xiàn)(線程安全) */ class Singleton{ private static final ThreadLocal<Singleton> singleton = new ThreadLocal<Singleton>(){ @Override protected Singleton initialValue(){ return new Singleton(); } }; private Singleton(){ } public static Singleton getSingleton(){ return singleton.get(); } }
使用CAS 鎖實(shí)現(xiàn)(線程安全)
/** * @author MrRoot * @since 2019-3-27 * 使用 CAS 實(shí)現(xiàn)(線程安全) */ public class Singleton { private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>(); private Singleton(){ } public static final Singleton getSingleton(){ for (;;){ Singleton current = INSTANCE.get(); if (current != null){ return current; } current = new Singleton(); if (INSTANCE.compareAndSet(null,current)){ return current; } } } public static void main(String[] args) { Singleton singleton1 = Singleton.getSingleton(); Singleton singleton2 = Singleton.getSingleton(); System.out.println(singleton1 == singleton2); } }
以上所述是小編給大家介紹的9種Java單例模式詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
Spring Cloud Gateway 內(nèi)存溢出的解決方案
這篇文章主要介紹了Spring Cloud Gateway 內(nèi)存溢出的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07spring boot基于DRUID實(shí)現(xiàn)數(shù)據(jù)源監(jiān)控過程解析
這篇文章主要介紹了spring boot基于DRUID實(shí)現(xiàn)數(shù)據(jù)源監(jiān)控過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12spring-boot-starter-web更換默認(rèn)Tomcat容器的方法
Spring Boot支持容器的自動配置,默認(rèn)是Tomcat,當(dāng)然我們也是可以進(jìn)行修改的。下面小編給大家?guī)砹藄pring-boot-starter-web更換默認(rèn)Tomcat容器的方法,感興趣的朋友跟隨小編一起看看吧2019-04-04spring AOP實(shí)現(xiàn)@Around輸出請求參數(shù)和返回參數(shù)
這篇文章主要介紹了spring AOP實(shí)現(xiàn)@Around輸出請求參數(shù)和返回參數(shù),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02解決idea默認(rèn)帶的equals和hashcode引起的bug
這篇文章主要介紹了解決idea默認(rèn)帶的equals和hashcode引起的bug,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07