JAVA 雙重檢查鎖定和延遲初始化

來源:互聯網
上載者:User

標籤:class   欄位   基於   程式   語義   tor   同步   線程   賦值   

雙重檢查鎖定的由來
在Java程式中,有時需要延遲一些高開銷的對象的初始化操作,並且只有在真正使用到這個對象的時候,才進行初始化,此時,就需要延遲初始化技術。
延遲初始化的正確實現是需要一些技巧的,否則容易出現問題,下面一一介紹。

方案1

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

這種做法的錯誤是很明顯的,如果兩個線程分別調用getInstance,由於對共用變數的訪問沒有做同步,很容易出現下面兩種情況:
1.線程A和B都看到instance沒有初始化,於是分別進行了初始化。
2.instance=new Instance操作被重排序,實際執行過程可能是:先分配記憶體,然後賦值給instance,最後再執行初始化。
如果是這樣的話,其他線程可能就會讀取到尚未初始化完成的instance對象。

方案2

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

這種做法的問題是很明顯的,每一次讀取instance都需要同步,可能會對效能產生較大的影響。

方案3

方案3是一個錯誤的雙重檢測加鎖實現,看代碼:

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

這種方案看似解決了上面兩種方案都存在的問題,但是也是有問題的。

問題根源

instance = new Instance();

這一條語句在實際執行中,可能會被拆分程三條語句,如下:

memory = allocate(); ctorInstance(memory); //2 instance = memory; //3 

根據重定序,後兩條語句不存在資料依賴,因此是可以進行重排序的。
重排序之後,就意味著,instance域在被賦值了之後,指向的對象可能尚未初始化完成,而instance域是一個靜態域,
可以被其他線程讀取到,那麼其他線程就可以讀取到尚未初始化完成的instance域。

基於volatile的解決方案

要解決這個辦法,只需要禁止語句2和語句3進行重排序即可,因此可以使用volatile來修改instance就能做到了。

private volatile static Instance instance;

因為Volatile語義會禁止編譯器將volatile寫之前的操作重排序到volatile之後。

基於類初始化的解決方案

Java語言規範規定,對於每一個類或者介面C ,都有一個唯一的初始化鎖LC與之對應,從C到LC的映射,由JVM實現。
每個線程在讀取一個類的資訊時,如果此類尚未初始化,則嘗試擷取LC去初始化,如果擷取失敗則等待其他線程釋放LC。
如果能擷取到LC,則要判斷類的初始化狀態,如果是位初始化,則要進行初始化。如果是正在初始化,
則要等待其他線程初始化完成,如果是已經初始化,則直接使用此類對象。

public class InstanceFactory{     private static class InstanceHolder{         public static Instance = new Instance();      }           public static Instance getInstance(){         return InstanceHolder.instance; //這裡將導致instance類被初始化     }     } 

結論

欄位延遲初始化降低了初始化類或者建立執行個體的開銷,但是增加零訪問被延遲促使化的欄位的開銷。
在大部分時候,正常的初始化要優於延遲初始化。如果確實需要對執行個體欄位使用安全執行緒的延遲初始化,
請使用上面介紹的基於volatile的延遲初始化方案;如果確實需要對靜態欄位使用安全執行緒的延遲初始化,
請使用上面基於類初始化方案的延遲初始化。

JAVA 雙重檢查鎖定和延遲初始化

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.