摘要:代碼分析構(gòu)造函數(shù)私有化,防止外部直接調(diào)用構(gòu)造函數(shù)通過改靜態(tài)方法獲取單例對象這是典型的懶漢式單例方法,低并發(fā)的情況下不會出現(xiàn)問題,若系統(tǒng)壓力增大,并發(fā)量增加將有非常大的可能創(chuàng)建多個(gè)實(shí)例。
前言
終于到周末了,又玩起了最愛的lol,最近新版本出了一個(gè)特別的天賦--偷錢(具體名字想不起來了),配上ez簡直是吊炸天,我玩的單排,僅用了不到三十分鐘就殺的對面出不了家,正當(dāng)我看著傷害板沾沾自喜,對面ad說不就是選了個(gè)版本最強(qiáng)ad,有什么了不起的...本帥這種具有理想抱負(fù)的屌絲當(dāng)然不會和這群傻叉多說什么,關(guān)閉了游戲,就來寫這篇文章,構(gòu)思的過程中想到,lol單排不能選重復(fù)英雄的機(jī)制不就是今天要講的單例模式嗎!
什么是單例模式確保某一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。單例模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn): 1. 既然是單例模式,就說明內(nèi)存中只存在一個(gè)實(shí)例,當(dāng)一個(gè)對象需要頻繁創(chuàng)建銷毀時(shí),而創(chuàng)建銷毀對象是虛擬機(jī)控制的,我們無法直接優(yōu)化其性能,單例模式的優(yōu)勢的就會體現(xiàn)的很明顯。 缺點(diǎn): 1.單例模式一般是沒有接口的,因?yàn)樗荒J(rèn)要求自行實(shí)例化。 2.單例模式對測試也是不利的,在并行開發(fā)環(huán)境下,假如我們的單例代碼還沒有開發(fā)好,沒有接口也不能通過mock的方式虛擬一個(gè)對象。 3.單例模式和上節(jié)講的單一原則有點(diǎn)沖突,因?yàn)閱卫O(shè)計(jì)模式把必須單例當(dāng)作一種業(yè)務(wù)和其他業(yè)務(wù)邏輯融入到一個(gè)類中。使用場景
對象是無狀態(tài)的,也就是說一個(gè)對象和多個(gè)對象辦的事都是一樣的,這樣的話,我們就有必要減少內(nèi)存使用和gc的消耗。
當(dāng)創(chuàng)建一個(gè)對象消耗的資源過于昂貴,比如通過io流讀寫文件、連接數(shù)據(jù)庫等資源。
代碼分析public class EZ { private static EZ ez = null; //構(gòu)造函數(shù)私有化,防止外部直接調(diào)用構(gòu)造函數(shù) private EZ() { } //通過改靜態(tài)方法獲取單例對象 public static EZ getSingletonEz() { if (ez == null) { return new EZ(); } return ez; } }
這是典型的懶漢式單例方法,低并發(fā)的情況下不會出現(xiàn)問題,若系統(tǒng)壓力增大,并發(fā)量增加將有非常大的可能創(chuàng)建多個(gè)實(shí)例。我們可以自己創(chuàng)建線程池,自己偽造一個(gè)并發(fā)環(huán)境,代碼如下:
public class SingletonTest { private static ExecutorService executorService = Executors.newFixedThreadPool(1000); public static void main(String[] args) throws InterruptedException { //給系統(tǒng)足夠的時(shí)候啟動(dòng)1000個(gè)線程 Thread.sleep(5000); final SetezSet = new HashSet<>(); executorService.submit(new Runnable() { @Override public void run() { ezSet.add(EZ.getSingletonEz().toString()); } }); Thread.sleep(5000); //若創(chuàng)建多個(gè)對象,size>1 System.out.println(ezSet.size()); } }
這個(gè)例子在公司的時(shí)候有測試過,大概運(yùn)行七八次就可以重現(xiàn)一次創(chuàng)建多個(gè)對象的現(xiàn)象。無奈現(xiàn)在家中,電腦配置過高,試了20次,竟一次都沒有重現(xiàn)。哎~。~,配置高有時(shí)也挺不方便的,你們電腦不好的可以試下?_?;既然存在并發(fā)情況創(chuàng)建多個(gè)對象問題,那我們就有必要改善下:
public class BetterEz { private static BetterEz ez = null; //構(gòu)造函數(shù)私有化,防止外部直接調(diào)用構(gòu)造函數(shù) private BetterEz() { } //通過改靜態(tài)方法獲取單例對象 public static BetterEz getSingletonEz() { if (ez == null) { synchronized (BetterEz.class) { if (ez == null) return new BetterEz(); } } return ez; } }
我們通過synchronized控制同一時(shí)間只有一個(gè)請求可以訪問同步代碼塊,但為什么又要在內(nèi)部加一層判斷呢?假如線程A、B同時(shí)請求,A獲得鎖執(zhí)行,B等待。當(dāng)A執(zhí)行完,B獲得鎖再去執(zhí)行,若沒有這個(gè)判斷豈不又重新創(chuàng)建了新對象。但這種典型的double-check的懶漢單例存在了一個(gè)可能發(fā)生的問題, jvm接收到new指令時(shí),簡單分為3步(實(shí)際更多,可參考深入理解虛擬機(jī)),1分配內(nèi)存2實(shí)例化對象3將內(nèi)存地址指向引用。java的內(nèi)存模型并不限制指令的重排序,也就說當(dāng)執(zhí)行步驟從1-》2-》3變成1-》3-》2。當(dāng)線程a訪問走到第2步,未完成實(shí)例化對象前,線程b訪問此對象的返回一個(gè)引用,但若是進(jìn)行其他操作,因?yàn)閷ο蟛]有實(shí)例化,會造成this逃逸的問題。jdk1.5之后對violate進(jìn)行了重新解釋,使用violate會強(qiáng)制保證線程可見性,增加內(nèi)存屏障,禁止指令優(yōu)化,從而避免這個(gè)問題?;蛘卟捎灭I漢式創(chuàng)建對象。
上面說的都是懶漢式單例,還有一種是餓漢式單例。懶漢式的優(yōu)點(diǎn)在于當(dāng)一次食用才會new對象,餓漢式的優(yōu)點(diǎn)在于在第一次獲取該對象的效率更高。下面我們看一下餓漢式單例:
public class EZ { private static final EZ ez = new EZ(); private EZ() { } public static EZ getSingletonEz() { return ez; } }
當(dāng)類第一次初始化的時(shí)候,就會對靜態(tài)域賦值。而jvm對于類的初始化進(jìn)行了嚴(yán)格規(guī)定,有且只有5種情況必須對類進(jìn)行初始化,關(guān)于類加載的問題不屬于這篇博客的范疇,就不多說了我們只需理解為這是靜態(tài)變量只會初始化一次,并且這是虛擬機(jī)保證的即可。
以上就是我對單例模式的理解,感謝各位的收看
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/68041.html
摘要:用來指向已創(chuàng)建好的實(shí)例構(gòu)造函數(shù)為空注意這里是關(guān)鍵這是我們需要調(diào)用的方法把函數(shù)也定義為空,這樣就大功告成啦。 接上一篇大話PHP設(shè)計(jì)模式之單例模式 這一篇介紹一下升級版的單例模式,廢話不說先上代碼 不完美的單例模式 class singleMode { //用來指向已創(chuàng)建好的實(shí)例 public static $instance; //判斷是...
摘要:博主按每天一個(gè)設(shè)計(jì)模式旨在初步領(lǐng)會設(shè)計(jì)模式的精髓,目前采用靠這吃飯和純粹喜歡兩種語言實(shí)現(xiàn)。單例模式用途如果一個(gè)類負(fù)責(zé)連接數(shù)據(jù)庫的線程池日志記錄邏輯等等,此時(shí)需要單例模式來保證對象不被重復(fù)創(chuàng)建,以達(dá)到降低開銷的目的。 博主按:《每天一個(gè)設(shè)計(jì)模式》旨在初步領(lǐng)會設(shè)計(jì)模式的精髓,目前采用javascript(_靠這吃飯_)和python(_純粹喜歡_)兩種語言實(shí)現(xiàn)。誠然,每種設(shè)計(jì)模式都有多種實(shí)...
摘要:博主按每天一個(gè)設(shè)計(jì)模式旨在初步領(lǐng)會設(shè)計(jì)模式的精髓,目前采用靠這吃飯和純粹喜歡兩種語言實(shí)現(xiàn)。單例模式用途如果一個(gè)類負(fù)責(zé)連接數(shù)據(jù)庫的線程池日志記錄邏輯等等,此時(shí)需要單例模式來保證對象不被重復(fù)創(chuàng)建,以達(dá)到降低開銷的目的。 博主按:《每天一個(gè)設(shè)計(jì)模式》旨在初步領(lǐng)會設(shè)計(jì)模式的精髓,目前采用javascript(_靠這吃飯_)和python(_純粹喜歡_)兩種語言實(shí)現(xiàn)。誠然,每種設(shè)計(jì)模式都有多種實(shí)...
摘要:上面是簡單的單例模式,自己寫程序的話夠用了,如果想繼續(xù)延伸,請傳送至大話設(shè)計(jì)模式之單例模式升級版 看了那么多單例的介紹,都是上來就說怎么做,也沒見說為什么這么做的。那小的就來說說為什么會有單例這個(gè)模式以便更好的幫助初學(xué)者真正的理解這個(gè)設(shè)計(jì)模式,如果你是大神,也不妨看完指正一下O(∩_∩)O首先我不得不吐槽一下這個(gè)模式名字單例,初學(xué)者通過字面很難理解什么是單例,我覺得應(yīng)該叫唯一模式更貼切...
摘要:最近開展了三次設(shè)計(jì)模式的公開課,現(xiàn)在來總結(jié)一下設(shè)計(jì)模式在中的應(yīng)用,這是第一篇?jiǎng)?chuàng)建型模式之單例模式。不過因?yàn)椴恢С侄嗑€程所以不需要考慮這個(gè)問題了。 最近開展了三次設(shè)計(jì)模式的公開課,現(xiàn)在來總結(jié)一下設(shè)計(jì)模式在PHP中的應(yīng)用,這是第一篇?jiǎng)?chuàng)建型模式之單例模式。 一、設(shè)計(jì)模式簡介 首先我們來認(rèn)識一下什么是設(shè)計(jì)模式: 設(shè)計(jì)模式是一套被反復(fù)使用、容易被他人理解的、可靠的代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。 設(shè)計(jì)模式不...
摘要:原文博客地址單例模式系統(tǒng)中被唯一使用,一個(gè)類只有一個(gè)實(shí)例。中的單例模式利用閉包實(shí)現(xiàn)了私有變量兩者是否相等弱類型,沒有私有方法,使用者還是可以直接一個(gè),也會有方法分割線不是單例最簡單的單例模式,就是對象。 原文博客地址:https://finget.github.io/2018/11/06/single/ 單例模式 系統(tǒng)中被唯一使用,一個(gè)類只有一個(gè)實(shí)例。實(shí)現(xiàn)方法一般是先判斷實(shí)例是否存在,...
閱讀 2603·2019-08-30 10:53
閱讀 3203·2019-08-29 16:20
閱讀 2962·2019-08-29 15:35
閱讀 1782·2019-08-29 12:24
閱讀 2890·2019-08-28 18:19
閱讀 1871·2019-08-23 18:07
閱讀 2352·2019-08-23 15:31
閱讀 1183·2019-08-23 14:05