日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java并发中的延迟初始化

發布時間:2023/12/15 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java并发中的延迟初始化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
不安全的延遲初始化示例:

?

Java代碼 ?
  • public?class?UnsafeLazyInitialization?{??
  • ????private?static?Resource?resource;??
  • ??
  • ????public?static?Resource?getInstance()?{??
  • ????????if?(resource?==?null)??
  • ????????????resource?=?new?Resource();?//?unsafe?publication??
  • ????????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并发中的延迟初始化的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。