最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Java中如何正確書寫單例模式

 更新時(shí)間:2017年01月04日 14:46:31   作者:又是火星人  
一般單例都是五種寫法:懶漢,餓漢,雙重校驗(yàn)鎖,靜態(tài)內(nèi)部類和枚舉。本文整理了幾種常見的單例寫法,下面跟著小編一起來(lái)看下吧

單例模式算是設(shè)計(jì)模式中最容易理解,也是最容易手寫代碼的模式,但是其中涉及的知識(shí)點(diǎn)卻一點(diǎn)也不少,所以經(jīng)常作為面試題來(lái)考。一般單例都是五種寫法:懶漢,餓漢,雙重校驗(yàn)鎖,靜態(tài)內(nèi)部類和枚舉。為了記錄學(xué)習(xí)過(guò)程的過(guò)程,這里整理了幾種常見的單例寫法,

青銅5:(Lazy-loaded,但線程不安全)

當(dāng)被問(wèn)到要實(shí)現(xiàn)一個(gè)單例模式時(shí),很多人的第一反應(yīng)是寫出如下的代碼,包括教科書上也是這樣教我們的。

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

這段代碼簡(jiǎn)單明了,而且使用了延遲加載模式,但是線程不安全。多線程環(huán)境下調(diào)用 getInstance() 方法,可能會(huì)發(fā)生多個(gè)線程進(jìn)入if語(yǔ)句的程序代碼塊。

懶漢式:synchronized(Lazy-loaded,線程安全,但不高效)

為了解決上面的問(wèn)題,最簡(jiǎn)單的方法是將整個(gè) getInstance() 方法設(shè)為同步(synchronized)。

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

雖然做到了線程安全、延遲加載,但是它并不高效。因?yàn)樵谌魏螘r(shí)候只能有一個(gè)線程調(diào)用 getInstance() 方法。但是synchronized操作只需要在第一次調(diào)用時(shí)才被需要,即第一次創(chuàng)建單例實(shí)例對(duì)象時(shí)。這種模式導(dǎo)致即使在單例創(chuàng)建完成后,每次依然只有一個(gè)線程可以訪問(wèn)getInstance()方法,會(huì)導(dǎo)致潛在的性能問(wèn)題。這就引出了雙重檢驗(yàn)鎖。

餓漢式:static final field(非Lazy-loaded)

這種方法非常簡(jiǎn)單,因?yàn)閱卫膶?shí)例被聲明成 static final,在第一次加載類到內(nèi)存中時(shí)就會(huì)初始化,所以創(chuàng)建實(shí)例對(duì)象是線程安全的(由JVM實(shí)現(xiàn)保證)。

public class Singleton{
 //類加載時(shí)就初始化
 private static final Singleton instance = new Singleton();
 private Singleton(){}
 public static Singleton getInstance(){  // Singleton with static factory
 return instance;
 }
}

它不是一種懶加載模式,instance會(huì)在加載類后一開始就被初始化,即使客戶端沒(méi)有調(diào)用 getInstance()方法。這會(huì)導(dǎo)致一些使用限制:譬如 Singleton 實(shí)例的創(chuàng)建是依賴參數(shù)或者配置文件的,在 getInstance() 之前必須調(diào)用某個(gè)方法設(shè)置參數(shù)給它,那樣這種單例寫法就無(wú)法使用了。類似的方法還有:

public class Singleton{
 public static final Singleton instance = new Singleton(); // Singleton with public final field
 private Singleton(){}
}
// <Effective Java>第14頁(yè)講訴了兩者的差異

雙重檢驗(yàn)鎖 + volatile(Lazyload,線程安全,但晦澀)

雙重檢驗(yàn)鎖模式(double checked locking pattern),是一種使用同步塊加鎖的方法。程序員稱其為雙重檢查鎖,因?yàn)闀?huì)有兩次檢查 instance == null,一次是在同步塊外,一次是在同步塊內(nèi)。為什么在同步塊內(nèi)還要再檢驗(yàn)一次?因?yàn)榭赡軙?huì)有多個(gè)線程一起進(jìn)入同步塊外的 if,如果在同步塊內(nèi)不進(jìn)行二次檢驗(yàn)的話就會(huì)生成多個(gè)實(shí)例對(duì)象了。

public static Singleton getSingleton() {
 if (instance == null) {       //Single Checked
  synchronized (Singleton.class) {
   if (instance == null) {     //Double Checked
    instance = new Singleton();
   }
  }
 }
 return instance ;
}

這段代碼看起來(lái)很完美,很可惜它是有問(wèn)題的。主要在于instance = new Singleton()這句,這并非是一個(gè)原子操作,事實(shí)上在 JVM 中這句話大概做了下面 3 件事情。

  1. 給 instance 分配內(nèi)存
  2. 調(diào)用 Singleton 的構(gòu)造函數(shù)來(lái)初始化成員變量
  3. 將instance對(duì)象指向分配的內(nèi)存空間(執(zhí)行完這步 instance 就為非 null 了)

但是在 JVM 的JIT編譯器中存在指令重排序的優(yōu)化。也就是說(shuō)上面的第二步和第三步的順序是不能保證的,最終的執(zhí)行順序可能是 1-2-3 也可能是 1-3-2。如果是后者,則在 3 執(zhí)行完畢、2未執(zhí)行之前,被線程二搶占了,這時(shí) instance 已經(jīng)是非 null 了(但卻沒(méi)有初始化),所以線程二會(huì)直接返回 instance,然后使用,然后順理成章地報(bào)錯(cuò)。為此,我們需要將 instance 變量聲明成 volatile 。

public class Singleton {
 private volatile static Singleton instance; //聲明為 volatile
 private Singleton(){}
 public static Singleton getSingleton() {
  if (instance == null) {       
   synchronized (Singleton.class) {
    if (instance == null) {  
     instance = new Singleton();
    }
   }
  }
  return instance;
 } 
}

但是特別注意在 Java 1.5 以前的版本使用了 volatile 的雙檢鎖還是有問(wèn)題的,這個(gè)問(wèn)題在 Java 1.5 中才得以修復(fù),所以在這之后才可以放心使用 volatile。

靜態(tài)內(nèi)部類:IoDH,initialization-on-demand holder

這個(gè)模式綜合使用了Java的靜態(tài)內(nèi)部類和多線程缺省同步鎖的知識(shí),很巧妙地同時(shí)實(shí)現(xiàn)了延遲加載和線程安全。

public class Singleton {
 private Singleton() {}
 private static class LazyHolder {
  private static final Singleton INSTANCE = new Singleton();
 }
 public static Singleton getInstance() {   // From wikipedia
  return LazyHolder.INSTANCE;
 }
}

靜態(tài)內(nèi)部類相當(dāng)于其外部類的static部分,它的對(duì)象不依賴于外部類對(duì)象,因此可以直接創(chuàng)建。靜態(tài)內(nèi)部類只有在第一次被使用的時(shí)候才會(huì)被轉(zhuǎn)載。

多線程缺省同步鎖

 大家都知道,在多線程開發(fā)中,為了解決并發(fā)問(wèn)題,主要是通過(guò)使用synchronized來(lái)加互斥鎖進(jìn)行同步控制。但是在某些情況中,JVM已經(jīng)隱含地為您執(zhí)行了同步,這些情況下就不需要手動(dòng)進(jìn)行同步控制了。這些情況包括

   1.由靜態(tài)初始化器(在靜態(tài)字段上或static{}塊中的初始化器)初始化數(shù)據(jù)時(shí)

 2.訪問(wèn)final字段時(shí)

 3.在創(chuàng)建線程之前創(chuàng)建對(duì)象時(shí)

 4.線程可以看見它將要處理的對(duì)象時(shí)

枚舉 Enum

從Java 1.5起,只需編寫一個(gè)包含單個(gè)元素的枚舉類型:

public enum Singleton {
 INSTANCE;
}

這種方法在功能上與公有域方法相近,但是它更加簡(jiǎn)潔,無(wú)償?shù)靥峁┝诵蛄谢瘷C(jī)制,絕對(duì)防止多次實(shí)例化,即使是在面向復(fù)雜的序列化或者反射攻擊的時(shí)候。雖然這種方法還沒(méi)有廣泛采用,但是單元素的枚舉類型以及成為實(shí)現(xiàn)Singleton的最佳方法。

--------------------以下是幾個(gè)細(xì)節(jié)存疑的實(shí)現(xiàn)方法---------------------

1.static final到底有哪些細(xì)節(jié)

2.static field處的賦值初始化到底和static代碼塊有先后嗎?

3.靜態(tài)內(nèi)部類的單例模式到底怎么寫

4.P50 in Java EE設(shè)計(jì)模式解析與應(yīng)用中的例子真的有Lazyload效果嗎?

以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!

相關(guān)文章

  • Java8排序stream.sorted()的使用

    Java8排序stream.sorted()的使用

    這篇文章主要介紹了Java8排序stream.sorted()的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • MyBatis傳入數(shù)組集合類并使用foreach遍歷

    MyBatis傳入數(shù)組集合類并使用foreach遍歷

    這篇文章主要介紹了MyBatis傳入數(shù)組集合類并使用foreach遍歷,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • SpringCloud-Alibaba-Sentinel-配置持久化策略詳解

    SpringCloud-Alibaba-Sentinel-配置持久化策略詳解

    這篇文章主要介紹了SpringCloud-Alibaba-Sentinel-配置持久化策略,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • Python安裝Jupyter Notebook配置使用教程詳解

    Python安裝Jupyter Notebook配置使用教程詳解

    這篇文章主要介紹了Python安裝Jupyter Notebook配置使用教程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java實(shí)現(xiàn)文件的加密解密功能示例

    Java實(shí)現(xiàn)文件的加密解密功能示例

    這篇文章主要介紹了Java實(shí)現(xiàn)文件的加密解密功能,結(jié)合具體實(shí)例形式詳細(xì)分析了java針對(duì)文件的讀取、判斷、加密、解密等相關(guān)步驟與操作技巧,需要的朋友可以參考下
    2017-10-10
  • Java?Swing的層次結(jié)構(gòu)深入理解

    Java?Swing的層次結(jié)構(gòu)深入理解

    這篇文章主要介紹了Java?Swing的層次結(jié)構(gòu)深入理解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • java中子類繼承父類,程序運(yùn)行順序的深入分析

    java中子類繼承父類,程序運(yùn)行順序的深入分析

    本篇文章是對(duì)java中子類繼承父類,程序運(yùn)行順序進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • SpringMVC實(shí)現(xiàn)注解式權(quán)限驗(yàn)證的實(shí)例

    SpringMVC實(shí)現(xiàn)注解式權(quán)限驗(yàn)證的實(shí)例

    本篇文章主要介紹了SpringMVC實(shí)現(xiàn)注解式權(quán)限驗(yàn)證的實(shí)例,可以使用Spring MVC中的action攔截器來(lái)實(shí)現(xiàn),具有一定的參考價(jià)值,有興趣的可以了解下。
    2017-02-02
  • SWT(JFace)體驗(yàn)之圓環(huán)狀(戒指型)

    SWT(JFace)體驗(yàn)之圓環(huán)狀(戒指型)

    SWT(JFace)體驗(yàn)之圓環(huán)狀(戒指型)實(shí)現(xiàn)代碼。
    2009-06-06
  • 使用Jackson來(lái)實(shí)現(xiàn)Java對(duì)象與JSON的相互轉(zhuǎn)換的教程

    使用Jackson來(lái)實(shí)現(xiàn)Java對(duì)象與JSON的相互轉(zhuǎn)換的教程

    這篇文章主要介紹了使用Jackson來(lái)實(shí)現(xiàn)Java對(duì)象與JSON的互相轉(zhuǎn)換的教程,文中羅列了3中Jackson的使用方式,需要的朋友可以參考下
    2016-01-01

最新評(píng)論

沙雅县| 绵竹市| 乌拉特后旗| 井冈山市| 板桥市| 长岛县| 湘阴县| 泸西县| 沧州市| 绩溪县| 雅江县| 桂阳县| 湘阴县| 平阴县| 长丰县| 吉安市| 濮阳市| 耒阳市| 沂南县| 郓城县| 安阳县| 杭锦后旗| 双鸭山市| 霍林郭勒市| 大宁县| 西青区| 乐陵市| 湖南省| 吉安市| 锦州市| 阿克陶县| 大庆市| 临夏县| 台中市| 札达县| 百色市| 沁水县| 思茅市| 崇义县| 通州市| 吴堡县|