單例模式 分析代碼優(yōu)化方法
單例模式是23種設(shè)計(jì)模式之一,是比較簡單的一種設(shè)計(jì)模式,它的目的是無論調(diào)用多少次,都返回同一個(gè)對象,它的特點(diǎn)是構(gòu)造器私有化。
它分為兩種結(jié)構(gòu),一種是懶漢式的,一種是餓漢式的,它們各有優(yōu)缺點(diǎn),我們先從餓漢式看起,代碼如下:
public class Single { private static Single single = new Single(); private Single() { } public Single getInstance() { return single; } }
通過上面的程序可以看出來雖然我們加載同一個(gè)對象的目的確實(shí)達(dá)到了,但當(dāng)程序被加載的時(shí)候就會創(chuàng)建single這個(gè)對象,當(dāng)這個(gè)類有多個(gè)這樣的方法時(shí),我們可能會用不到這個(gè)對象中大多數(shù)單例,就會造成對內(nèi)存的浪費(fèi)。所以就出現(xiàn)了懶漢式的單例模式,代碼如下:
public class Single { private static Single single = null; private Single() { } public Single getInstance() { if(single==null){ single = new Single(); } return single; } }
這樣,只有當(dāng)我們真正調(diào)用這個(gè)對象時(shí)它才會被new出來,但是這樣是存在問題的。
當(dāng)上面的第二段代碼在第一次加載的時(shí)候有兩個(gè)線程對其進(jìn)行了調(diào)用,則會產(chǎn)生兩個(gè)不同的對象,所以是線程不安全的,這時(shí)候就會想到給這個(gè)方法加個(gè)鎖,加鎖之后的代碼如下:
public class Single { private static Single single = null; private Single() { } public synchronized Single getInstance() { if (single == null) { single = new Single(); } return single; } }
這樣做確實(shí)做到了線程安全,但是當(dāng)加鎖這個(gè)方法里面要執(zhí)行很多東西,調(diào)用這個(gè)方法花費(fèi)的時(shí)間會很長,這樣對服務(wù)器來說是致命的,因?yàn)檫@個(gè)方法如果某個(gè)線程一直調(diào)用的話,其它的線程是沒有辦法調(diào)的,服務(wù)器就阻塞了,那么升級后的代碼如下:
public class Single { priate static Single single = null; private Single() { } public Single getInstance() { if (single == null) { synchronized (Single.class) { single = new Single(); } } return single; } }
仔細(xì)觀察以后發(fā)現(xiàn)這樣并沒有鎖住,當(dāng)?shù)谝淮瓮瑫r(shí)有兩個(gè)線程到達(dá)getInstance()方法if判斷時(shí),其中有一個(gè)肯定是阻塞的,當(dāng)另外一個(gè)執(zhí)行完以后,阻塞這個(gè)線程是不會再判斷是否為空的,還是會創(chuàng)建一個(gè)對象的,這樣又有多個(gè)對象被產(chǎn)生了,再對其進(jìn)行升級,得到的代碼如下:
public class Single { private static Single single = null; private Single() { } public Single getInstance() { if (single == null) { synchronized (Single.class) { if (single == null) { single = new Single(); } } } return single; } }
這樣就不會產(chǎn)生上面的問題,而且也只鎖一次,因?yàn)榈诙卧賵?zhí)行這個(gè)方法時(shí),會跳過if判斷,直接返回single,不會再被鎖,執(zhí)行效率也會很高。
但即使是這樣,也還是有問題的,因?yàn)槲覀儾荒艽_定在內(nèi)存中是先給對象賦值,還是先創(chuàng)建了這個(gè)對象,所以第二個(gè)程序有可能得到的是初始化一半了的對象,在jdk1.5之后,我們可以用volatile這個(gè)關(guān)鍵字來避免這種情況,代碼如下:
public class Single { private static volatile Single single = null; private Single() { } public Single getInstance() { if (single == null) { synchronized (Single.class) { if (single == null) { single = new Single(); } } } return single; } }
但是這種情況很少使用,我在這里只是為了學(xué)習(xí)一下,嘻嘻
相關(guān)文章
java實(shí)現(xiàn)二維碼掃碼授權(quán)登陸
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)二維碼掃碼授權(quán)登陸,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-10-10SVN導(dǎo)入maven項(xiàng)目報(bào)錯解決方案
這篇文章主要介紹了SVN導(dǎo)入maven項(xiàng)目報(bào)錯解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12SpringBoot整合Minio實(shí)現(xiàn)上傳文件的完整步驟記錄
MinIO是一個(gè)基于Apache License v2.0開源協(xié)議的對象存儲服務(wù),它兼容亞馬遜S3云存儲服務(wù)接口,非常適合于存儲大容量非結(jié)構(gòu)化的數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于SpringBoot整合Minio實(shí)現(xiàn)上傳文件的完整步驟,需要的朋友可以參考下2022-05-05JDBC PreparedStatement Like參數(shù)報(bào)錯解決方案
這篇文章主要介紹了JDBC PreparedStatement Like參數(shù)報(bào)錯解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10淺談SpringBoot之開啟數(shù)據(jù)庫遷移的FlyWay使用
這篇文章主要介紹了淺談SpringBoot之開啟數(shù)據(jù)庫遷移的FlyWay使用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01AsyncConfigurerSupport自定義異步線程池處理異常
這篇文章主要為大家介紹了AsyncConfigurerSupport自定義異步線程池處理異常詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06