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