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