生活随笔
收集整理的這篇文章主要介紹了
java并发中的延迟初始化
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
不安全的延遲初始化示例:
?
Java代碼 ?
public?class?UnsafeLazyInitialization?{??????private?static?Resource?resource;????????public?static?Resource?getInstance()?{??????????if?(resource?==?null)??????????????resource?=?new?Resource();???????????return?resource;??????}????????static?class?Resource?{??????}??}??
? 一般情況下,估計大家都會這么寫代碼,在非并發環境中,這個getInstance方法會工作的很好,但是放到并發環境中,問題就來了,比如競態條件,但這里還存在另外一個問題,即另一個線程可能會看到對部分構造的Resource實例的引用。
簡單點來說,就是線程A在訪問getInstance方法時,發現resource為null,于是就resource設置為一個新實例,在這個過程中,線程B也調用getInstance方法,發現resource不為空,因此就直接使用這個resource實例了。看起來貌似沒有問題,但是因為線程A實例化resource的操作和線程B讀取resource實例的操作之間不存在Happens-Before關系,所以,在線程B使用resource實例時,resource實例也許還未構造完成,這就導致了線程B看到的resource實例不正確的狀態。
?
解決這個問題的一個簡單辦法就是使用同步,這也是我們經常會使用的辦法:
?
Java代碼 ?
public?class?SafeLazyInitialization?{??????private?static?Resource?resource;????????public?synchronized?static?Resource?getInstance()?{??????????if?(resource?==?null)??????????????resource?=?new?Resource();??????????return?resource;??????}????????static?class?Resource?{??????}??}??
? 這種辦法夠簡單直接,但是在getInstance方法被頻繁調用的時候,還是會存在激烈的競爭。書中還給出了另外一種辦法,就是不采用延遲初始化,也就是提前初始化,在定義resource時候,就實例化它:
?
Java代碼 ?
public?class?EagerInitialization?{??????private?static?Resource?resource?=?new?Resource();????????public?static?Resource?getResource()?{??????????return?resource;??????}????????static?class?Resource?{??????}??}??
?
? 這種辦法在日常開發中也會采用到。但是這種辦法為什么是線程安全的呢?這涉及到JVM在類的初始化階段給出的線程安全性保證。因為JVM在類初始化階段,會獲取一個鎖,并且每個線程都會至少獲取一次這個鎖以確保這個類已經加載,在靜態初始化期間,內存的寫入操作自動對所有線程可見,而resource的初始化就是屬于靜態初始化。因此,在構造期間或者被引用時,靜態初始化的對象都不需要顯式的同步,但是這個規則只適用于在構造時的狀態,如果對象可變,那么在其它地方對該對象的訪問還是需要使用同步來確保對對象的修改操作是可見的。
?
下面再來看看另一種解決辦法,書中稱之為延遲初始化占位類模式,我認為這個方法很巧妙:
?
?
Java代碼 ?
public?class?ResourceFactory?{??????private?static?class?ResourceHolder?{??????????public?static?Resource?resource?=?new?Resource();??????}????????public?static?Resource?getResource()?{??????????return?ResourceFactory.ResourceHolder.resource;??????}????????static?class?Resource?{??????}??}??
?
? 這種方法就是基于上述JVM在類的初始化階段給出的線程安全性保證,將resource的實例化操作放置到一個靜態內部類中,在第一次調用getResource方法時,JVM才會去加載ResourceHelper類,同時初始化resource實例,因此,即使我們不采取任何同步策略,getResource方法也是線程安全的。
?
后面還有講到基于雙重檢查鎖(DCL)的方式來實現,但是這種方法屬于糟糕的方法,這里就不過多描述了,示例代碼如下:
?
Java代碼 ?
public?class?DoubleCheckedLocking?{??????private?static?Resource?resource;????????public?static?Resource?getInstance()?{??????????if?(resource?==?null)?{??????????????synchronized?(DoubleCheckedLocking.class)?{??????????????????if?(resource?==?null)??????????????????????resource?=?new?Resource();??????????????}??????????}??????????return?resource;??????}????????static?class?Resource?{????????}??}?
總結
以上是生活随笔為你收集整理的java并发中的延迟初始化的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。