Java單例模式的學習筆記
Java
舉個不嚴謹的通俗例子,我們用的電腦,只能配一個鍵盤,不論誰用,都只能通過這一個鍵盤進行輸入,你說我非要用倆鍵盤,小明這邊正敲代碼,小張那邊寫文檔,沒必要還亂了套。
枚舉
網上一搜單例模式的代碼,上去肯定是什麼餓漢懶漢,我先上個牛的。
枚舉
為什麼牛呢?我只用一句話解釋:這是Josh Bloch推薦的。不知道這位大神的出門左拐,面壁思過。
懶漢
懶漢
這段代碼應該是單例模式的Hello World了,一看就懂,想必很多人學習單例就是從這個開始的。代碼、邏輯很清晰,很能說明問題,就是線程不安全,舉個栗子打個樣可以,實際工作中不要用。
餓漢
餓漢
這個代碼也很清晰,因為static final,在類載入的時候就把對象實例化了,又是線程安全。
這種寫法還有個變種,就是new Singleton3()這一塊用static塊包起來,效果一樣,不寫了。
懶漢,線程安全
之前說的懶漢線程不安全,如果要求線程安全的話,大家第一反應肯定就是加鎖。
線程安全的懶漢
就是在實例化的方法上加了同步鎖,線程安全是做到了,但是大家都知道synchronized太耗資源了,沒這個必要吧~~
雙重校驗鎖
synchronized太耗資源了,它包含的代碼越少越好,那麼我們只需要把實例化這一塊代碼包住不就行了?
雙重校驗鎖
第一次校驗的時候沒有同步鎖,如果已經實例化了,直接返回即可,省下了同步鎖要消耗的資源。第二次校驗是因為第一次校驗沒有同步鎖,所以多個線程都可能進入,同步塊內二重校驗一下,保證不會生成多個實例。
但是這樣其實還有隱患,就在於sl=new Singleton5()這一行,這不是一個原子操作!
類初始化步驟是
1、給sl分配內存
2、調用Singleton5的構造函數初始化
3、將sl的對象指向分配好的內存空間
在第3步,這個sl就不是null了。
但是在多線程情況下,JVM很雞賊的做了優化——指令重排序,簡單說就是,JVM一旦覺得第2步花的時間長,就先把第3步給執行了。
對於上面的代碼來說,先分配內存,然後不等初始化,直接指向分配好的內存,sl非null了!第二個線程過來一看是非null,那就直接返回,但是因為沒有初始化,所以一用就會報錯。
所以做如下改動即可:
private volatile static Singleton5 sl;
volatile可以禁止指令重排序優化。
JDK5之前這麼用還是有問題,JDK5已經修復了,可以放心大膽使用。
靜態內部類
靜態內部類
多解釋兩句,總體思想還是懶漢思維,採用一個靜態內部類(SingletonLazyHolder),因為JVM的機制(靜態內部類被引用才載入),使得SingletonLazyHolder只在調用getInstance()方法時,因為SingletonLazyHolder.INSTANCE這行代碼,產生了對它的引用才載入進去,而且天然的線程安全,達到線程安全的懶漢效果。
測試類
測試類
結果
結果
※虛擬機VBox安裝CentOS6.8,內外網訪問
※CentOS6.8安裝MySQL5.7
※ZooKeeper單機版安裝配置——集群版後面再發
※MySQL5.7主從複製
TAG:Java個人學習心得 |
※static 單例模式autoptr與單例模式const 用法小結mutable修飾符
※Python 中的單例模式
※Surface Phone將支持「筆記本模式」
※Flutter 的編譯模式
※刨銑acessemarketing商業模式
※「Just a line」讓你在AR模式下繪畫塗鴉
※Chrome OS多屏模式下更像Windows
※web api之selfhost模式
※Scott Brinker:數字改變營銷運作模式
※Ghost Recon Wildlands更新將使用Permadeath 模式
※Today Steam:Steam賬戶隱私調整 《星露穀物語》多人模式免費更新
※Photoshop使用混合模式繪製蘋果圖標
※LeBron James 正式進入季後賽模式: 我將試著主宰一切
※Google Assistant登陸iPad平板:支持多任務模式
※網路專家解讀YouTube,Twitter或Reddit的盈利模式
※谷歌AR Sticker改為Playground,作為Pixel相機新模式
※微軟Outlook.com beta上線暗黑模式
※手腕上運動專家,15種模式指導你運動更科學—Garmin vívoactive3 戶外運動腕錶評測
※蘋果iPhonex如何進入dfu模式?蘋果iPhonex進入恢復模式操作方法
※雙HomePod立體聲模式叫做FullRoom