国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

單例模式與多線程

2019-11-08 02:55:52
字體:
來源:轉載
供稿:網友

單例模式都不陌生,有餓漢式單例、懶漢式單例等。

餓漢式單例

餓漢式單例:

public class Singleton { PRivate static Singleton instance = new Singleton(); public static Singleton getInstance(){ return instance; }}

在類第一次加載的時候,單例就完成了初始化。下面來驗證餓漢式單例的線程安全性:

public class MyThread extends Thread{ public void run() { System.out.println(Singleton.getInstance().hashCode()); }}public class Test { public static void main(String[] args) throws Exception { Thread t1 = new MyThread(); Thread t2 = new MyThread(); Thread t3 = new MyThread(); t1.start(); t2.start(); t3.start(); }}

運行得到:

763347431 763347431 763347431

三次得到的 hashcode() 返回值都一樣。


結論:餓漢式單例在類第一次加載的時候完成初始化,是線程安全的。

懶漢式單例

懶漢式單例:

public class Singleton { private static Singleton instance = null; public static Singleton getInstance(){ if(instance == null){ // 1 instance = new Singleton(); // 2 } // 3 return instance; }}

運用了延遲加載,在需要的時候進行初始化。 然而 1、2、3 整體不具有原子性,所以懶漢式單例應該不是線程安全的。


下面證明懶漢式單例是非線程安全的:

public class MyThread extends Thread{ public void run() { System.out.println(Singleton.getInstance().hashCode()); }}public class Test { public static void main(String[] args) throws Exception { Thread t1 = new MyThread(); Thread t2 = new MyThread(); Thread t3 = new MyThread(); t1.start(); t2.start(); t3.start(); }}

運行得到:

2146509683 810456228 1996534722


結論:懶漢式單例運用延遲加載在需要時候進行初始化,保證了特定情況下其性能要優于餓漢式單例。然而它卻是非線程安全的。

雙重檢查鎖定機制

我們嘗試修改代碼,目的是把懶漢式單例修改成線程安全的。

第一次嘗試 為 getInstance() 加鎖:

public class Singleton { private static Singleton instance = null; synchronized public static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; }}

運行得到:

2000544445 2000544445 2000544445

這樣修改可以保證線程安全性;但由于鎖的獨占性,多個線程頻繁調用 getIntance() 很可能會阻塞,效率低下。


第二次嘗試 繼續縮小同步塊的范圍:

public class Singleton { private static Singleton instance = null; public static Singleton getInstance() { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } return instance; }}

運行得到:

hashcode() 返回值都一樣。

這種方法基本等同于第一次嘗試。


第三次嘗試 繼續縮小同步塊的范圍:

public class Singleton { private static Singleton instance = null; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { instance = new Singleton(); } } return instance; }}

運行得到:

1175759956 2000544445 2146509683

相比前兩次嘗試,第三次嘗試執行效率會有明顯提升;但是破壞了原子性,不是線程安全的,得到的可能不是單例。


第四次嘗試 雙重檢查鎖定機制(double check lock)(DCL):

public class Singleton { private static Singleton instance = null; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }}

運行得到:

hashcode() 返回值都一樣。

此方案綜合了前面幾次嘗試的優點:

鎖同步保證了原子性,保證線程安全性;第一次空檢測對提升性能起到了很大的作用。

但是真的實現了線程安全嗎?并沒有。

volatile 修正 DCL

在 DCL 基礎上,為變量 instance 加上 voltile 關鍵字,才算是真正的實現了線程安全:

public class Singleton { private volatile static Singleton instance = null; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }}

運行得到:

hashcode()返回值都一樣。


Q:回頭來看,為什么必須加 volatile 關鍵字呢? A:重排序


結論:懶漢式單例,經過基于 volatile 的 DCL 進行改造,能夠具有線程安全性。

volatile + synchronized 實現了線程安全性。DCL 的第一個空檢測很大程度上優化了性能

final 修正 DCL

增加一個 final 域同樣能解決問題:

public class Singleton { private static Singleton instance = null; private final int para; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } private Singleton(){ para = 1; }}

final 域的內存語義

寫內存語義:在構造函數內對一個 final 域的寫入,與隨后將對象引用賦值給引用變量,這兩個操作不能重排序; 實現原理:在 final 域的寫之后,構造函數 return 之前,插入一個 StoreStore 屏障; 寫內存語義可以確保在對象的引用為任意線程可見之前,final 域已經被初始化過了。

讀內存語義:初次讀一個包含 final 域的對象的引用,與隨后初次讀這個 final 域,這兩個操作不能重排序; 實現原理:在讀 final 域之前插入一個 LoadLoad 屏障。 讀內存語義可以確保如果對象的引用不為 null,則說明 final 域已經被初始化過了。

總之,final 域的內存語義提供了初始化安全保證:只要 this 引用沒有在構造函數中“逸出”,不需要同步就可以保證任意線程看到的都是初始化后的值。 注意:此時 Singleton 并非不可變,引入 final 域的目的是能夠安全地初始化。

靜態內部類實現單例模式

public class Singleton { private static class Holder{ private static Singleton instance = new Singleton(); } public static Singleton getInstance() { return Holder.instance; }}

調用 getInstance() 導致 Holder 類被裝載,Holder 對應的的 Class 類型的對象自動創建,JVM 會獲得這個 Class 對象初始化鎖,這個鎖可以同步多個線程對同一個類的初始化。因此指令重排還是可能發生的,但是并不影響獲得初始化鎖的下一個線程,因為下一個線程進來的時候,上個線程已經完成了類的初始化。

看圖更形象一些:

這里寫圖片描述

可能得到的執行時序:

這里寫圖片描述


結論:靜態內部類實現的單例模式能夠保證線程安全,同時具有延遲加載特性。而且代碼夠簡潔哦。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 筠连县| 囊谦县| 关岭| 微山县| 清远市| 青川县| 大余县| 汶上县| 古蔺县| 金溪县| 清苑县| 哈巴河县| 福海县| 沅陵县| 老河口市| 舒城县| 杂多县| 黔南| 昆山市| 宜宾市| 巴林左旗| 广东省| 两当县| 天峻县| 治多县| 彭山县| 莫力| 临安市| 云林县| 灌阳县| 随州市| 海阳市| 天镇县| 云南省| 安平县| 阳西县| 武隆县| 巴塘县| 华蓥市| 屏南县| 海伦市|