摘要:?jiǎn)卫J礁攀鰡卫J绞且环N對(duì)象創(chuàng)建模式,用于產(chǎn)生一個(gè)類的具體事例。所以解決了線程安全問題參考失效原因和解決方案中單例模式的缺陷及單例的正確寫法懶漢式靜態(tài)內(nèi)部類私有構(gòu)造器獲取單例的方法靜態(tài)內(nèi)部類持有單例作為靜態(tài)屬性。
單例模式概述
單例模式是一種對(duì)象創(chuàng)建模式,用于產(chǎn)生一個(gè)類的具體事例。使用單例模式可以確保整個(gè)系統(tǒng)中單例類只產(chǎn)生一個(gè)實(shí)例。有下面兩大好處:
對(duì)于頻繁創(chuàng)建的對(duì)象,節(jié)省初第一次實(shí)例化之后的創(chuàng)建時(shí)間。
由于new操作的減少,會(huì)降低系統(tǒng)內(nèi)存的使用頻率。減輕GC壓力,從而縮短GC停頓時(shí)間
創(chuàng)建方式:
單例作為類的私有private屬性
單例類擁有私有private構(gòu)造函數(shù)
提供獲取實(shí)例的public方法
單例模式的角色:
角色 | 作用 |
---|---|
單例類 | 提供單例的工廠,返回類的單例實(shí)例 |
使用者 | 獲取并使用單例類 |
類基本結(jié)構(gòu):
public class HungerSingleton { //1.餓漢式 //私有構(gòu)造器 private HungerSingleton() { System.out.println("create HungerSingleton"); } //私有單例屬性 private static HungerSingleton instance = new HungerSingleton(); //獲取單例的方法 public static HungerSingleton getInstance() { return instance; } }
注意:
單例修飾符為static JVM加載單例類加載時(shí),直接初始化單例。無法延時(shí)加載。如果此單例一直未被使用,單Singleton 因?yàn)檎{(diào)用靜態(tài)方法被初始化則會(huì)造成內(nèi)存的浪費(fèi)。
getInstance()使用static修飾,不用實(shí)例化可以直接使用Singleton.getInstance()獲取單例。
由于單例由JVM加載類的時(shí)候創(chuàng)建,所以不存在線程安全問題。
2.簡(jiǎn)單懶漢式public class Singleton { //2.1簡(jiǎn)單懶漢式(線程不安全) //私有構(gòu)造器 private Singleton() { System.out.println("create Singleton"); } //私有單例屬性[初始化為null] private static Singleton instance = null; //獲取單例的方法 public static Singleton getInstance() { if(instance == null) { //此處instance實(shí)例化 //首次調(diào)用單例時(shí)會(huì)進(jìn)入 達(dá)成延時(shí)加載 instance = new Singleton(); } return instance; } }
由于未使用 synchronized 關(guān)鍵字,所以當(dāng)線程1調(diào)用單例工廠方法Singleton.getInstance() 且 instance 未初始化完成時(shí),線程2調(diào)用此方法會(huì)將instance判斷為null,也會(huì)將instance重新實(shí)例化賦值,此時(shí)則產(chǎn)生了多個(gè)實(shí)例!
如需線程安全可以直接給getInstance方法上加synchronized關(guān)鍵字,如下:
public class Singleton { //2.2簡(jiǎn)單懶漢式(線程安全) //私有構(gòu)造器 private Singleton() { System.out.println("create Singleton"); } //私有單例屬性[初始化為null] private static Singleton instance = null; //獲取單例的方法 將此方法使用synchronized關(guān)鍵字同步 public static synchronized Singleton getInstance() { if(instance == null) { //此處instance實(shí)例化 //首次調(diào)用單例時(shí)會(huì)進(jìn)入 達(dá)成延時(shí)加載 instance = new Singleton(); } return instance; } }
面臨的問題:
由于對(duì)getInstance()整個(gè)方法加鎖,在多線程的環(huán)境中性能比較差。
3.DCL 懶漢式(雙重檢測(cè))簡(jiǎn)單懶漢式(線程安全)中,對(duì)getInstance()方法加鎖,導(dǎo)致多線程中性能較差,那么是否可以減小鎖的范圍,使不用每次調(diào)用geInstance()方法時(shí)候都會(huì)去競(jìng)爭(zhēng)鎖?DCL(Double Check Locking)雙重檢測(cè) 就是這樣一種實(shí)現(xiàn)方式。
public class DCLLazySingleton { //3.DCL //私有構(gòu)造器 private DCLLazySingleton() { System.out.println("create DCLLazySingleton"); } //私有單例屬性[初始化為null] volatile 保證內(nèi)存可見性 防止指令重排 private static volatile DCLLazySingleton instance = null;//step1 //獲取單例的方法 public static DCLLazySingleton getInstance() { //這里判null 是為了在instance有值時(shí),不進(jìn)入加鎖的代碼塊,提高代碼性能。 if(instance == null) { //縮小鎖范圍 由于是靜態(tài)方法方法調(diào)用的時(shí)候不依賴于實(shí)例化的對(duì)象 加鎖只能使用類 synchronized (DCLLazySingleton.class) { //這里判null 是為了配合volatile解決多線程安全問題 if(instance == null) { instance = new DCLLazySingleton(); } } } return instance; } }
注意:
傳統(tǒng)DCL(step1處并未使用 volatile 或使用了但在JDK1.8之前)面臨的問題:
由于初始化單例對(duì)象new DCLLazySingleton() 操作并不是原子操作,由于這是很多條指令,jvm可能會(huì)亂序執(zhí)行。
在線程1初始化對(duì)象可能并未完成,但是此時(shí)已經(jīng)instance對(duì)象已經(jīng)不為null。(已經(jīng)分配了內(nèi)存,但是構(gòu)造方法還未執(zhí)行完【可能有一些屬性的賦值未執(zhí)行】)
此時(shí)線程2再獲取instance 則不為null 直接返回。那么此時(shí)線程2獲取的則為‘構(gòu)造方法未執(zhí)行完的instance對(duì)象’。則不能保證線程安全。
解決方式:
加上volatile關(guān)鍵字,volatile保證內(nèi)存可見性,內(nèi)存屏障,防止指令排!
加上volatile關(guān)鍵字后,線程2獲取的構(gòu)造方法未執(zhí)行完的instance對(duì)象,會(huì)在線程1修改之后同步到線程2(volatile 內(nèi)存空間)。所以解決了線程安全問題
參考:
DCL失效原因和解決方案
java 中單例模式DCL的缺陷及單例的正確寫法
4.懶漢式(靜態(tài)內(nèi)部類)public class StaticSingleton { //私有構(gòu)造器 private StaticSingleton() { System.out.println("create StaticSingleton!"); } //獲取單例的方法 public static StaticSingleton getInstance() { return SingletonHolder.instance; } //靜態(tài)內(nèi)部類 持有單例 作為靜態(tài)屬性。 //由于只有在訪問屬性時(shí)才會(huì)加載靜態(tài)類初始化instance。所以實(shí)現(xiàn)了懶加載。且由于JVM保證了類的加載為線程安全,所以為線程安全的。 private static class SingletonHolder { //私有單例屬性 private static StaticSingleton instance = new StaticSingleton(); } }
注意:
由于StaticSingleton類被加載時(shí),內(nèi)部的私有靜態(tài)類SingletonHolder并不會(huì)被加載,所以并不會(huì)初始化單例instance,當(dāng)getInstance()被調(diào)用時(shí)SingletonHolder.instance 才會(huì)加載SingletonHolder,由于JVM保證了類的加載為線程安全,因此線程安全。
此方式既可以做到延時(shí)加載,也不會(huì)因?yàn)橥疥P(guān)鍵字影響性能。是一種比較完善的實(shí)現(xiàn)。推薦使用
5.枚舉單例public enum EnumSingleton { INSTANCE(); EnumSingleton() { System.out.println("create EnumSingleton"); } }
線程安全,且能夠抵御反射與序列化。
推薦使用
例外情況上述的單例實(shí)現(xiàn)方式還是會(huì)面臨一些特殊情況不能保證唯一實(shí)例:
反射調(diào)用私有構(gòu)造方法。
序列化后反序列化會(huì)生成多個(gè)對(duì)象??梢詫?shí)現(xiàn)私有readResolve方法。readObject()如同虛設(shè),直接使用readResolve替換原本返回值。如下:
private Object readResolve () { //返回當(dāng)前對(duì)象 return instance; }
由于上述兩情況比較特殊,所以沒有特別關(guān)注。
參考書籍《Java程序性能優(yōu)化》 -葛一鳴 等編著
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/75921.html
摘要:如果需要防范這種攻擊,請(qǐng)修改構(gòu)造函數(shù),使其在被要求創(chuàng)建第二個(gè)實(shí)例時(shí)拋出異常。單例模式與單一職責(zé)原則有沖突。源碼地址參考文獻(xiàn)設(shè)計(jì)模式之禪 定義 單例模式是一個(gè)比較簡(jiǎn)單的模式,其定義如下: 保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。 或者 Ensure a class has only one instance, and provide a global point of ac...
摘要:所以,在版本前,雙重檢查鎖形式的單例模式是無法保證線程安全的。 單例模式可能是代碼最少的模式了,但是少不一定意味著簡(jiǎn)單,想要用好、用對(duì)單例模式,還真得費(fèi)一番腦筋。本文對(duì)Java中常見的單例模式寫法做了一個(gè)總結(jié),如有錯(cuò)漏之處,懇請(qǐng)讀者指正。 餓漢法 顧名思義,餓漢法就是在第一次引用該類的時(shí)候就創(chuàng)建對(duì)象實(shí)例,而不管實(shí)際是否需要?jiǎng)?chuàng)建。代碼如下: public class Singleton...
摘要:總結(jié)單例是運(yùn)用頻率很高的模式,因?yàn)榭蛻舳藳]有高并發(fā)的情況,選擇哪種方式并不會(huì)有太大的影響,出于效率考慮,推薦使用和靜態(tài)內(nèi)部類實(shí)現(xiàn)單例模式。 單例模式介紹 單例模式是應(yīng)用最廣的模式之一,也可能是很多人唯一會(huì)使用的設(shè)計(jì)模式。在應(yīng)用單例模式時(shí),單例對(duì)象的類必須保證只用一個(gè)實(shí)例存在。許多時(shí)候整個(gè)系統(tǒng)只需要一個(gè)全局對(duì)象,這樣有利于我么能協(xié)調(diào)整個(gè)系統(tǒng)整體的行為。 單例模式的使用場(chǎng)景 確保某個(gè)類有且...
摘要:?jiǎn)卫J娇梢员苊鈱?duì)資源的多重重用。單例模式可以在系統(tǒng)中設(shè)置全局的訪問點(diǎn),優(yōu)化和共享資源訪問。一個(gè)簡(jiǎn)單的單例模式場(chǎng)景運(yùn)行結(jié)果一個(gè)管理多個(gè)單例的數(shù)組場(chǎng)景運(yùn)行結(jié)果 單例模式的優(yōu)缺點(diǎn): 1 單例模式只能在內(nèi)存中存在一個(gè)實(shí)例,減少了內(nèi)存開支,特別是對(duì)一個(gè)對(duì)象需要頻繁的創(chuàng)建和銷毀時(shí),而且創(chuàng)建和銷毀又不能進(jìn)行優(yōu)化時(shí),單例模式的優(yōu)勢(shì)就非常明顯。 2 由于單例只生成一個(gè)實(shí)例,減少了系統(tǒng)的性能開銷,當(dāng)一...
閱讀 3327·2021-11-19 09:40
閱讀 3060·2021-09-09 09:32
閱讀 873·2021-09-02 09:55
閱讀 1454·2019-08-26 13:23
閱讀 2530·2019-08-26 11:46
閱讀 1297·2019-08-26 10:19
閱讀 2136·2019-08-23 16:53
閱讀 1129·2019-08-23 12:44