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