自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

雙重檢查鎖定失敗可能性

開發(fā) 后端
雙重檢查鎖定在延遲初始化的單例模式中見得比較多(單例模式實現(xiàn)方式很多,這里為說明雙重檢查鎖定問題,只選取這一種方式).

雙重檢查鎖定在延遲初始化的單例模式中見得比較多(單例模式實現(xiàn)方式很多,這里為說明雙重檢查鎖定問題,只選取這一種方式),先來看一個版本:

 

  1. public class Singleton {  
  2.  
  3.     private static Singleton instance = null;  
  4.  
  5.     private Singleton(){}  
  6.  
  7.       
  8.  
  9.     public static Singleton  getInstance() {  
  10.  
  11.        if(instance == null) {  
  12.  
  13.            instance = new Singleton();  
  14.  
  15.        }  
  16.  
  17.        return instance;  
  18.  
  19.     }  
  20.  
  21. }  

 

上面是最原始的模式,一眼就可以看出,在多線程環(huán)境下,可能會產(chǎn)生多個Singleton實例,于是有了其同步的版本:

 

  1. public class Singleton {  
  2.  
  3.     private static Singleton instance = null;  
  4.  
  5.     private Singleton(){}  
  6.  
  7.       
  8.  
  9.     public synchronized static Singleton getInstance() {  
  10.  
  11.        if(instance == null) {  
  12.  
  13.            instance = new Singleton();  
  14.  
  15.        }  
  16.  
  17.        return instance;  
  18.  
  19.     }  
  20.  
  21. }  

 

在這個版本中,每次調(diào)用getInstance都需要取得Singleton.class上的鎖,然而該鎖只是在開始構(gòu)建Singleton 對象的時候才是必要的,后續(xù)的多線程訪問,效率會降低,于是有了接下來的版本:

 

  1. public class Singleton {  
  2.  
  3.     private static Singleton instance = null;  
  4.  
  5.     private Singleton(){}  
  6.  
  7.       
  8.  
  9.     public static Singleton getInstance() {  
  10.  
  11.        if(instance == null) {  
  12.  
  13.            synchronized(Singleton.class) {  
  14.  
  15.               if(instance == null) {  
  16.  
  17.                   instance = new Singleton();  
  18.  
  19.               }  
  20.  
  21.            }  
  22.  
  23.        }  
  24.  
  25.        return instance;  
  26.  
  27.     }  
  28.  
  29. }  

 

很好的想法!不幸的是,該方案也未能解決問題之根本:

原因在于:初始化Singleton 和 將對象地址寫到instance字段 的順序是不確定的。在某個線程new Singleton()時,在構(gòu)造方法被調(diào)用之前,就為該對象分配了內(nèi)存空間并將對象的字段設(shè)置為默認(rèn)值。此時就可以將分配的內(nèi)存地址賦值給instance字段了,然而該對象可能還沒有初始化;此時若另外一個線程來調(diào)用getInstance,取到的就是狀態(tài)不正確的對象。

鑒于以上原因,有人可能提出下列解決方案:

 

  1. public class Singleton {  
  2.  
  3.     private static Singleton instance = null;  
  4.  
  5.     private Singleton(){}  
  6.  
  7.       
  8.  
  9.     public static Singleton getInstance() {  
  10.  
  11.        if(instance == null) {  
  12.  
  13.            Singleton temp;  
  14.  
  15.            synchronized(Singleton.class) {  
  16.  
  17.               temp = instance;  
  18.  
  19.               if(temp == null) {  
  20.  
  21.                   synchronized(Singleton.class) {  
  22.  
  23.                      temp = new Singleton();  
  24.  
  25.                   }  
  26.  
  27.                   instance = temp;  
  28.  
  29.               }  
  30.  
  31.            }  
  32.  
  33.        }  
  34.  
  35.        return instance;  
  36.  
  37.     }  
  38.  
  39. }  

 

該方案將Singleton對象的構(gòu)造置于最里面的同步塊,這種思想是在退出該同步塊時設(shè)置一個內(nèi)存屏障,以阻止初始化Singleton 和 將對象地址寫到instance字段 的重新排序。

不幸的是,這種想法也是錯誤的,同步的規(guī)則不是這樣的。退出監(jiān)視器(退出同步)的規(guī)則是:所以在退出監(jiān)視器前面的動作都必須在釋放監(jiān)視器之前完成。然而,并沒有規(guī)定說退出監(jiān)視器之后的動作不能放到退出監(jiān)視器之前完成。也就是說同步塊里的代碼必須在退出同步時完成,而同步塊后面的代碼則可以被編譯器或運(yùn)行時環(huán)境移到同步塊中執(zhí)行。

編譯器可以合法的,也是合理的,將instance = temp移動到最里層的同步塊內(nèi),這樣就出現(xiàn)了上個版本同樣的問題。

在JDK1.5及其后續(xù)版本中,擴(kuò)充了volatile語義,系統(tǒng)將不允許對 寫入一個volatile變量的操作與其之前的任何讀寫操作 重新排序,也不允許將 讀取一個volatile變量的操作與其之后的任何讀寫操作 重新排序。

在jdk1.5及其后的版本中,可以將instance 設(shè)置成volatile以讓雙重檢查鎖定生效,如下:

 

  1. public class Singleton {  
  2.  
  3.     private static volatile Singleton instance = null;  
  4.  
  5.     private Singleton(){}  
  6.  
  7.       
  8.  
  9.     public static Singleton getInstance() {  
  10.  
  11.        if(instance == null) {  
  12.  
  13.            synchronized(Singleton.class) {  
  14.  
  15.               if(instance == null) {  
  16.  
  17.                   instance = new Singleton();  
  18.  
  19.               }  
  20.  
  21.            }  
  22.  
  23.        }  
  24.  
  25.        return instance;  
  26.  
  27.     }  
  28.  
  29. }  

 

需要注意的是:在JDK1.4以及之前的版本中,該方式仍然有問題。

【編輯推薦】

  1. 有趣的Java對象序列化緩存問題
  2. 關(guān)于Java對象序列化您不知道的5件事
  3. Java 7 I/O新功能探秘:同步操作,多播與隨機(jī)存取
  4. Java實用技巧:當(dāng)不能拋出checked異常時
  5. 多線程開發(fā)的捷徑:構(gòu)建Java并發(fā)模型框架
責(zé)任編輯:金賀 來源: ITEYE博客
相關(guān)推薦

2016-11-11 00:33:25

雙重檢查鎖定延遲初始化線程

2022-12-30 07:40:12

DeepKitTypeScript靜態(tài)類型

2013-11-29 09:51:26

C++雙重檢查鎖定

2013-12-23 09:48:43

C++鎖定模式

2018-03-02 11:38:11

2016-09-21 09:16:55

Qlik

2011-04-18 13:43:42

2023-10-27 14:25:26

組件庫無限可能性

2017-07-21 16:40:29

網(wǎng)易云場景專屬云

2012-06-04 13:28:51

AndroidChrome OS

2021-02-20 12:04:51

比特幣區(qū)塊鏈美元

2019-04-22 08:57:46

硅谷996ICU

2019-04-15 10:30:38

程序員技能開發(fā)者

2009-03-11 18:27:04

Windows 7商業(yè)版

2020-08-11 09:38:40

微信蘋果美國

2011-04-18 13:47:59

ECC私鑰

2018-11-26 09:48:57

服務(wù)器異常宕機(jī)

2020-05-15 13:42:03

物聯(lián)網(wǎng)人工智能軍事物聯(lián)網(wǎng)

2013-03-19 11:13:14

Google廣告SXSW

2022-06-27 11:24:20

人工智能術(shù)語AI
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號