成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

有關雙檢測鎖定(DCL)和Singleton模式的問題

開發 后端
本文解釋了一個有關雙檢測鎖定(DCL)和Singleton模式的問題。在多線程的情況下Singleton模式會遇到不少問題,一個解決方案是雙檢測鎖定,不過事實上這種方法在JDK5之前會導致問題。

看OOP教材時,提到了一個雙檢測鎖定(Double-Checked Lock, DCL)的問題,但是書上沒有多介紹,只是說這是一個和底層內存機制有關的漏洞。查閱了下相關資料,對這個問題大致有了點了解。

從頭開始說吧。

在多線程的情況下Singleton模式會遇到不少問題,一個簡單的例子

1:  class Singleton {      
   2:      private static Singleton instance = null;     
   3:        
   4:      public static Singleton instance() {     
   5:          if (instance == null) {     
   6:              instance = new Singleton();     
   7:          }     
   8:          return instance;    
   9:      }    
   10:  }
  

假設這樣一個場景,有兩個線程調用Singleton.instance(),首先線程一判斷instance是否等于null,判斷完后一瞬間虛擬機把線程二調度為運行線程,線程二再次判斷instance是否為null,然后創建一個Singleton實例,線程二的時間片用完后,線程一被喚醒,接下來它執行的代碼依然是instance = new Singleton();
兩次調用返回了不同的對象,出現問題了。

最簡單的方法自然是在類被載入時就初始化這個對象:private static Singleton instance = new Singleton();

JLS(Java Language Specification)中規定了一個類只會被初始化一次,所以這樣做肯定是沒問題的。

但是如果要實現延遲初始化(Lazy initialization),比如這個實例初始化時的參數要在運行期才能確定,應該怎么做呢?

依然有最簡單的方法:使用synchronized關鍵字修飾初始化方法:

  1. public synchronized static Singleton instance() {          
  2.     if (instance == null) {  
  3.         instance = new Singleton();  
  4.     }  
  5.     return instance;  
  6. }   

這里有一個性能問題:多個線程同時訪問這個方法時,會因為同步而導致每次只有一個線程運行,影響程序性能。而事實上初始化完畢后只需要簡單的返回instance的引用就行了。

雙檢測鎖定解決方案

DCL是一個“看似”有效的解決方法,先把對應代碼放上來吧:

    1 :   class Singleton {   
    2 :       private static Singleton instance = null ;   
    3 :      
    4 :       public static Singleton instance() {   
    5 :           if (instance == null ) {
    6 :               synchronized (this) {   
    7 :                   if (instance == null)
    8 :                      instance = new Singleton();
    9 :              }
    10 :          }
    11 :          return instance;
    12 :      }
    13 :  }

用JavaWorld上對應文章的標題來評論這種做法就是smart, but broken。來看原因:

Java編譯器為了提高程序性能會進行指令調度,CPU在執行指令時同樣出于性能會亂序執行(至少現在用的大多數通用處理器都是out-of-order的),另外cache的存在也會改變數據回寫內存時的順序[2]。JMM(Java Memory Model, 見[1])指出所有的這些優化都是允許的,只要運行結果和嚴格按順序執行所得的結果一樣即可。

Java假設每個線程都跑在自己的處理器上,享有自己的內存,和共享的主存交互。注意即使在單核上這種模型也是有意義的,考慮到cache和寄存器會保存部分臨時變量。理論上每個線程修改自己的內存后,必須立即更新對應的主存內容。但是Java設計師們認為這種約束會影響程序性能,他們試著創造了一套讓程序跑得更快、但又保證線程之間的交互與預期一致的內存模型。

synchronized關鍵字便是其中一把利器。事實上,synchronized塊的實現和Linux中的信號量(semaphore)還是有區別的,前者過程中鎖的獲得和釋放都會都會引發一次Memory Barrier來強制線程本地內存和主存之間的同步。通過這個機制,Java中的同步機制保證了synchronized塊中指令的原子性(atomic)。

雙檢測鎖定的問題

好了,回過頭來看DCL問題。看起來訪問一個未同步的instance字段不會產生什么問題,我們再次來假設一個場景:

線程一進入同步塊,執行instance = new Singleton(); 線程二剛開始執行getResource();

按照順序的話,接下來應該執行的步驟是 1) 分配新的Singleton對象的內存 2) 調用Singleton的構造器,初始化成員字段 3) instance被賦為指向新的對象的引用。

前面說過,編譯器或處理器都為了提高性能都有可能進行指令的亂序執行,線程一的真正執行步驟可能是1) 分配內存 2) instance指向新對象 3) 初始化新實例。如果線程二在2完成后3執行前被喚醒,它看到了一個不為null的instance,跳出方法體走了,帶著一個還沒初始化的Singleton對象。

錯誤發生的一種情形就是這樣,關于更詳細的編譯器指令調度導致的問題,可以參看這個網頁 [4]。

[3] 中提供了一個編譯器指令調度的證據

instance = new Singleton(); 這條命令在Symantec JIT中被編譯成

  1. 0206106A   mov         eax,0F97E78h  
  2. 0206106F   call        01F6B210                  ; 分配空間  
  3. 02061074   mov         dword ptr [ebp],eax       ; EBP中保存了instance的地址   
  4.  
  5. 02061077   mov         ecx,dword ptr [eax]       ; 解引用,獲得新的指針地址   
  6.  
  7. 02061079   mov         dword ptr [ecx],100h      ; 接下來四行是inline后的構造器  
  8. 0206107F   mov         dword ptr [ecx+4],200h      
  9. 02061086   mov         dword ptr [ecx+8],400h  
  10. 0206108D   mov         dword ptr [ecx+0Ch],0F84030h   
  11.  

可以看到,賦值完成在初始化之前,而這是JLS允許的。
 
另一種情形是,假設線程一安穩地完成Singleton對象的初始化,退出了同步塊,并同步了和本地內存和主存。線程二來了,看到一個非空的引用,拿走。注意線程二沒有執行一個Read Barrier,因為它根本就沒進后面的同步塊。所以很有可能此時它看到的數據是陳舊的。

還有很多人根據已知的幾種提出了一個又一個fix的方法,但最終還是出現了更多的問題。可以參閱[3]中的介紹。

[5]中還說明了即使把instance字段聲明為volatile還是無法避免錯誤的原因。

由此可見,安全的Singleton的構造一般只有兩種方法,一是在類載入時就創建該實例,二是使用性能較差的synchronized方法。

本文來自ZelluX的BlogJava博客《Singleton模式與雙檢測鎖定(DCL) 》一文

參考資料:
[1] Java Language Specification, Second Edition, 第17章介紹了Java中線程和內存交互關系的具體細節。
[2] out-of-order與cache的介紹可以參閱Computer System, A Programmer's Perspective的第四、五章。
[3] The "Double-Checked Locking is Broken" Declaration
[4] Synchronization and the Java Memory Model
[5] Double-checked locking: Clever, but broken
[6] Holub on Patterns, Learning Design Patterns by Looking at Code

【編輯推薦】

  1. 最簡單的設計模式學習:Singleton模式
  2. 詳解Java Singleton模式的好處
  3. Java設計模式與應用淺談
  4. 設計模式在EJB中的應用
  5. 探討JSF框架中使用的設計模式
責任編輯:yangsai 來源: ZelluX的博客
相關推薦

2009-07-08 17:25:05

Java Single

2009-07-09 17:30:59

Singleton模式C++ SingletJava Single

2013-04-19 09:16:56

2010-02-06 10:04:10

Android啟動模式

2009-08-31 16:12:02

C#使用Singlet

2009-08-25 18:04:30

C#實現Singlet

2009-08-31 15:48:02

C# Singleto

2009-09-02 16:23:27

C# Singleto

2011-07-18 16:51:51

Cocoa 單態 模式

2012-08-22 10:10:25

單態單態設計設計模式

2010-07-14 14:20:47

IMAP協議服務

2009-08-12 13:22:44

Singleton模式

2016-12-26 17:57:13

戴爾成就篇

2009-09-24 16:55:27

策略模式

2021-03-18 18:07:57

JavaScript開發代碼

2018-10-30 16:39:12

雙頻WiFi雙路

2009-07-20 18:03:26

Scala程序Singleton對象

2010-03-09 16:38:22

Linux grub啟

2010-07-08 12:49:12

SIP會話發起協議

2009-06-11 13:00:08

Java數組賦值
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品国产一区一区二区三亚瑟 | 九色av | 国产精品视频一二三区 | 中文字幕一区在线观看视频 | 日本免费一区二区三区四区 | 精品九九 | 久久精品国产久精国产 | 亚洲欧美另类在线 | 久久伊人精品 | 久久久一区二区三区四区 | 午夜影院毛片 | 欧美日韩一区不卡 | 精品一区二区久久久久久久网精 | 日韩久久综合 | 色综合视频 | 黄色大片视频 | caoporon| 欧美一区二区三区在线观看 | 精品视频免费 | 亚洲天堂网站 | 殴美成人在线视频 | 国产成人福利 | 嫩草研究影院 | 亚洲成人三级 | 激情麻豆视频 | 五月天婷婷综合 | 午夜影视免费片在线观看 | 色偷偷888欧美精品久久久 | 一区在线视频 | 视频三区 | 伊人婷婷| 欧美精品乱码99久久影院 | 欧美激情在线观看一区二区三区 | 精品国产一区二区国模嫣然 | 成人伊人| 欧美日韩精品区 | 国产精品久久久久婷婷二区次 | 欧美日韩一区二区三区在线观看 | 精品国产精品国产偷麻豆 | 久久精品国产99国产 | 欧美精品一区在线发布 |