摘要:如何解救呢作為一個(gè)的開發(fā)者,對(duì)一定不陌生,提到多線程,大部分人想到的都是他后,他的性能提升巨大,解決簡(jiǎn)單并發(fā),非常適用。
消耗內(nèi)存最嚴(yán)重的對(duì)象創(chuàng)建過(guò)程,必須對(duì)其進(jìn)行約束,作為創(chuàng)建型模式的單例模式(Singleton),始終保持應(yīng)用程序中某一個(gè)實(shí)例有且僅有一個(gè),可以很顯著的提升程序性能。
單線程下的Singleton的穩(wěn)定性是極好的,可分為兩大類:
1.Eager(餓漢型): 類加載時(shí)立即創(chuàng)建對(duì)象。
public class EagerSingleton { //1. 類加載時(shí)就立即產(chǎn)生實(shí)例對(duì)象,通過(guò)設(shè)置靜態(tài)變量被外界獲取 //2. 并使用private保證封裝安全性 private static EagerSingleton eagerSingleton = new EagerSingleton(); //3. 通過(guò)構(gòu)造方法的私有化,不允許外部直接創(chuàng)建對(duì)象,確保單例的安全性 private EagerSingleton(){ } public static EagerSingleton getEagerSingleton(){ return eagerSingleton; }
2.Lazy(懶漢型):類加載時(shí)沒有立即創(chuàng)建對(duì)象,等到第一個(gè)用戶獲取才進(jìn)行實(shí)例化。
public class LazySingleton { //1. 類加載時(shí)并沒有創(chuàng)建唯一實(shí)例 private static LazySingleton lazySingleton; private LazySingleton() { } //2、提供一個(gè)獲取實(shí)例的靜態(tài)方法 public static LazySingleton getLazySingleton() { if (lazySingleton == null) { lazySingleton = new LazySingleton(); } return lazySingleton; }
就性能方面而言,LazySingleton 明顯優(yōu)于 EagerSingleton ,若類的加載需要耗費(fèi)大量的資源(e.g. 讀取大文件信息),那么LazySingleton 的優(yōu)勢(shì)顯而易見。但通過(guò)閱讀代碼,很容易發(fā)現(xiàn)一個(gè)致命問題。多線程間如何保持安全性?
下面將對(duì)多線程并發(fā)問題進(jìn)行解析:
解決該問題的關(guān)鍵在于兩方面:1.同步; 2.性能;
有線程A,線程B同時(shí)調(diào)用getLazySingleton()獲取實(shí)例,A調(diào)用時(shí)判斷instance為null,正準(zhǔn)備進(jìn)行初始化時(shí),突然A線程被掛起了,此時(shí)對(duì)象并未實(shí)例化成功,更糟的事隨后發(fā)生,B線程被運(yùn)行了,他也判斷了instance為null,此時(shí)A,B都進(jìn)入了實(shí)例化階段,這樣就產(chǎn)生了兩個(gè)實(shí)例,破壞單例原則。
如何解救呢?
作為一個(gè)java的開發(fā)者,對(duì)synchronized一定不陌生,提到多線程,大部分人想到的都是他(JDK6后,他的性能提升巨大,解決簡(jiǎn)單并發(fā),非常適用)。
那就讓我們用synchronized來(lái)嘗試解決吧:
//由synchronized進(jìn)行同步加鎖 public synchronized static LazySingleton getLazySingleton() { if (lazySingleton == null) { lazySingleton = new LazySingleton(); } return lazySingleton; }
如此同步問題看似解決,但是作為一個(gè)開發(fā)者,最重要的是性能的保障,使用synchronized有利有弊,由于加鎖操作,代碼段被加上悲觀鎖,只有等一個(gè)請(qǐng)求完成,下個(gè)請(qǐng)求才能進(jìn)入執(zhí)行。通常加上synchronized關(guān)鍵字的代碼片會(huì)比同等量級(jí)的代碼慢上幾倍,這是我們不愿見到的。那如何避免這一問題呢?在java對(duì)synchronized的定義里有這樣的建議:越遲使用synchronized,性能越優(yōu)(細(xì)化鎖)。
###### 2.因此,我們需要開始解決性能的問題了。按照synchronized優(yōu)化: ######
public class DoubleCheckLockSingleton { //使用volatile保證每次取值不是從緩存中取,而是從真正對(duì)應(yīng)的內(nèi)存地址中取.(下文解釋) private static volatile DoubleCheckLockSingleton doubleCheckLockSingleton; private DoubleCheckLockSingleton(){ } public static DoubleCheckLockSingleton getDoubleCheckLockSingleton(){ //配置雙重檢查鎖(下文解釋) if(doubleCheckLockSingleton == null){ synchronized (DoubleCheckLockSingleton.class) { if(doubleCheckLockSingleton == null){ doubleCheckLockSingleton = new DoubleCheckLockSingleton(); } } } return doubleCheckLockSingleton; } }
上述源碼就是經(jīng)典的volatile關(guān)鍵字(JDK1.5 后重生)+雙重檢查鎖(DoubleCheck),最大程度的優(yōu)化了sychronized帶來(lái)的性能開銷。下面將為大家解釋volatile與DoubleCheck。
1.volatile
是在JDK1.5后才正式被實(shí)現(xiàn)使用的,之前的版本只是定義了該關(guān)鍵字,未有具體實(shí)現(xiàn)。若想理解volatile就必須對(duì)JVM自身的內(nèi)存管理有些許了解:
1.1 遵循著摩爾定律,內(nèi)存的讀寫速度已遠(yuǎn)不能滿足CPU,因此現(xiàn)代計(jì)算機(jī)引入了在CPU上添加高速緩存的機(jī)制,由緩存預(yù)讀取內(nèi)存的值,并暫存于緩存中,通過(guò)計(jì)算,再更新內(nèi)存中的相應(yīng)值。
**1.2** 而JVM模仿PC的這一做法,在內(nèi)存中劃分了自己的**工作內(nèi)存**,該部分內(nèi)存作用與高速緩存一致,很顯著的提高JVM工作效率,但凡事都有利有弊,這一做法也導(dǎo)致工作內(nèi)存與其他內(nèi)存通信時(shí)容易導(dǎo)致傳輸上的問題。volatile的一個(gè)功能就是強(qiáng)制的從內(nèi)存中讀取最新的值,避免緩存與內(nèi)存不一致的狀況。
1.3 volatile的另一個(gè)功能也是和JVM相關(guān),即JVM會(huì)通過(guò)自身的判斷,將源碼的執(zhí)行順序重排,保證指令流水線連貫性,以達(dá)到最優(yōu)的執(zhí)行方案。這種做法提高了性能,但對(duì)DoubleCheck卻會(huì)產(chǎn)生意想外的結(jié)果,兩線程可能互相干擾。而volatile提供了happens-before guarantee(寫優(yōu)先于讀),使對(duì)象不被干擾,保證安全的穩(wěn)定性。
2.DoubleCheck
這是現(xiàn)代編程的遺留,假設(shè)進(jìn)入同步塊之后,對(duì)象已被實(shí)例化,此時(shí)需再次進(jìn)行判斷。
當(dāng)然還有一種官方推薦的單例實(shí)現(xiàn)方法:
由于類的構(gòu)造在定義中已是原子性的,因此上述的各種問題都不會(huì)再產(chǎn)生,是一種很好的單例實(shí)現(xiàn)方式,推薦使用。
//使用內(nèi)部類進(jìn)行單例構(gòu)造 public class NestedClassSingleton { private NestedClassSingleton(){ } private static class SingletonHolder{ private static final NestedClassSingleton nestedClassSingleton = new NestedClassSingleton(); } public static NestedClassSingleton getNestedClassSingleton(){ return SingletonHolder.nestedClassSingleton; } }
祝近安
ooooor
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/77563.html
摘要:總之,選擇單例模式就是為了避免不一致狀態(tài),避免政出多頭。二餓漢式單例餓漢式單例類在類初始化時(shí),已經(jīng)自行實(shí)例化靜態(tài)工廠方法餓漢式在類創(chuàng)建的同時(shí)就已經(jīng)創(chuàng)建好一個(gè)靜態(tài)的對(duì)象供系統(tǒng)使用,以后不再改變,所以天生是線程安全的。 概念: Java中單例模式是一種常見的設(shè)計(jì)模式,單例模式的寫法有好幾種,這里主要介紹兩種:懶漢式單例、餓漢式單例?! 卫J接幸韵绿攸c(diǎn): 1、單例類只能有一個(gè)實(shí)例。 ...
該文章屬于《編程中的那些經(jīng)典套路——設(shè)計(jì)模式匯總》系列,并且以下內(nèi)容基于語(yǔ)言PHP 在設(shè)計(jì)模式中,單例模式和工廠模式)可以說(shuō)是使用的最普遍的設(shè)計(jì)模式了,所以掌握此種模式尤為重要。 單例模式一般使用在資源共享和需要控制資源的情況下。 例如:購(gòu)物車,回收站,數(shù)據(jù)庫(kù)連接池,計(jì)數(shù)器,配置文件共享等所有項(xiàng)目中只需要存在一個(gè)的模塊,你都可以采用單例模式。 單例模式的好處就在于當(dāng)前進(jìn)程只產(chǎn)生一個(gè)對(duì)象(或者叫...
摘要:此時(shí)我們創(chuàng)建的對(duì)象內(nèi)保存靜態(tài)變量通過(guò)取值器訪問,最后將這個(gè)對(duì)象作為一個(gè)單例放在全局空間里面作為靜態(tài)變量單例對(duì)象供他人使用。 單例模式 又被稱為單體模式,是只允許實(shí)例化一次的對(duì)象類。有時(shí)我們也用一個(gè)對(duì)象來(lái)規(guī)劃一個(gè)命名空間,井井有條的管理對(duì)象上面的屬性和方法。 傳統(tǒng)的面向?qū)ο笳Z(yǔ)言中單例模式的實(shí)現(xiàn),均是單例對(duì)象從類中創(chuàng)建而來(lái),在以類為中心的語(yǔ)言中,這是很常見的做法。如果需要某個(gè)對(duì)象,就必須先...
摘要:?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í)際項(xiàng)目中像數(shù)據(jù)庫(kù)查詢,日志輸出,全局回調(diào),統(tǒng)一校驗(yàn)等模塊。單例模式的好處減少頻繁創(chuàng)建,節(jié)省了。因此在單例模式必須包含三要素私有化構(gòu)造函數(shù),私有化。 單例作為一個(gè)最經(jīng)典的設(shè)計(jì)模式之一,到底什么是單例?為什么要用單例?怎么設(shè)計(jì)單例?php中單例如何具體實(shí)現(xiàn)? 一、什么是單例 wiki百科:?jiǎn)卫J?,也叫單子模式,是一種常用的軟件設(shè)計(jì)模式。 在應(yīng)用這個(gè)模式時(shí),單例對(duì)象的類必須...
閱讀 2125·2021-09-29 09:35
閱讀 2003·2019-08-30 14:15
閱讀 3024·2019-08-30 10:56
閱讀 1014·2019-08-29 16:59
閱讀 634·2019-08-29 14:04
閱讀 1367·2019-08-29 12:30
閱讀 1085·2019-08-28 18:19
閱讀 562·2019-08-26 11:51