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