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

Java設(shè)計模式之單例模式詳解

 更新時間:2017年03月15日 17:14:28   作者:byhieg  
這篇文章主要為大家詳細(xì)介紹了Java設(shè)計模式之單例模式的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下

單例模式是非常常見的設(shè)計模式,其含義也很簡單,一個類給外部提供一個唯一的實例。下文所有的代碼均在github

源碼整個項目不僅僅有設(shè)計模式,還有其他JavaSE知識點,歡迎Star,F(xiàn)ork

單例模式的UML圖

單例模式的關(guān)鍵點

通過上面的UML圖,我們可以看出單例模式的特點如下:

1、構(gòu)造器是私有的,不允許外部的類調(diào)用構(gòu)造器
2、提供一個供外部訪問的方法,該方法返回單例類的實例

如何實現(xiàn)單例模式

上面已經(jīng)給出了單例模式的關(guān)鍵點,我們的實現(xiàn)只需要滿足上面2點即可。但是正因為單例模式的實現(xiàn)方式比較寬松,所以不同的實現(xiàn)方式會有不同的問題。我們可以對單例模式的實現(xiàn)做一下分類,看一看有哪些不同的實現(xiàn)方式。

1根據(jù)單例對象的創(chuàng)建時機(jī)不同,可以分為餓漢模式和懶漢模式。餓漢是指在類加載的時候,就創(chuàng)建了對象。但是創(chuàng)建對象有時比較消耗資源,會造成類加載很慢,但是優(yōu)點是獲取對象的速度很快,因為早已經(jīng)創(chuàng)建好了嘛。懶漢就是相對餓漢而言,在需要返回單例對象的時候,在創(chuàng)建對象,類加載的時候,并不初始化,好處與缺點也不言而喻

2.根據(jù)是否實現(xiàn)線程安全,可以分為普通的懶漢模式這種線程不安全的寫法,和餓漢模式,雙重檢查鎖的懶漢模式,以及通過靜態(tài)內(nèi)部類或者枚舉類等實現(xiàn)的線程安全的寫法。

一個線程不安全的單例模式

public class SimpleSingleton {

  private static SimpleSingleton simpleSingleton;

  private SimpleSingleton(){

  }

  public static SimpleSingleton getInstance(){
    if (simpleSingleton == null) {
      simpleSingleton = new SimpleSingleton();
    }
    return simpleSingleton;
  }
}

首先,我們可以看出這是一個懶漢模式的實現(xiàn)。因為只有在getInstance的時候,才會真正創(chuàng)建單例的對象。但是為什么他是線程不安全的呢,是因為可能會有2個線程同時進(jìn)入if (simpleSingleton == null)的判斷,就是同時創(chuàng)建了simpleSingleton對象。

DCL懶漢模式

上面的方法可以看出是存在線程不安全的問題的,我們可以用同步關(guān)鍵字synchronized來實現(xiàn)線程安全。我們先逐步分析,先用synchronized來改寫上面的懶漢模式,代碼如下:

public class DCLSingleton {

  private static DCLSingleton singleton;
  private DCLSingleton(){
  }

  public synchronized static DClSingleton getSingleton(){
    if (singleton == null) {
      singleton = new DCLSingleton();
    }
    return singleton;
  }

}

這樣,就有效的保證了不會有兩個線程同時執(zhí)行該方法,但這個效率也太低了吧。因為在創(chuàng)建實例之后,每次得到實例對象,還是需要進(jìn)行同步,synchronized的同步保證代價是比較大的,因此可以在此基礎(chǔ)上進(jìn)行改造。在已經(jīng)創(chuàng)建好之后,就不需要同步了,我們可以改成如下的形式:

  public static DCLSingleton getSingleton(){
    if (singleton == null) {
      synchronized (DCLSingleton.class) {
        if (singleton == null) {
          singleton = new DCLSingleton();
        }
      }
    }
    return singleton;
  }

其他代碼不變,只看這個方法。該方法的兩重if (singleton == null)可以有效地保證線程安全。比如,當(dāng)兩個線程同時進(jìn)入該方法的時候,第一個if,兩者都是進(jìn)入,下面的代碼,但是碰到同步代碼塊,只能有一個先進(jìn)入,進(jìn)入的時候,繼續(xù)判斷,再次判斷為空,才會真正創(chuàng)建對象。如果不進(jìn)行,第二個判斷,那些對于第一個進(jìn)入的線程而言,確實創(chuàng)建了對象,但是第二個線程,他緊接著也會執(zhí)行創(chuàng)建對象的操作,因為不知道第一個線程已經(jīng)創(chuàng)建成功。因此,需要兩次判空。
但是真的就如此簡單的保證了線程安全嗎?我們仔細(xì)分析一下這個過程,singleton = new DCLSingleton();這個代碼實際上是3個操作。

1.給DCLSingleton實例分配內(nèi)存
2.調(diào)用DCLSingleton()的構(gòu)造函數(shù),初始化成員字段
3.將singleton對象指向分配的內(nèi)存空間。

在JDK1.5以前,上面的3個執(zhí)行順序是不固定的,有可能是1-2-3,或者1-3-2。如果是1-3-2,則在第一個線程執(zhí)行完第三步以后,第二個線程立即執(zhí)行,但還沒有真正的進(jìn)行初始化,所以就會使用的時候出錯。在JDK1.5以后,我們可以用volatile關(guān)鍵字來保證該1-2-3的順序執(zhí)行。所以,除了getSingleton()方法要改成上面的樣子以外,還需要對private static DCLSingleton singleton; 改寫成private static volatile DCLSingleton singleton; 這樣,就真正保證了線程同步的懶漢寫法的單例模式。

餓漢寫法

餓漢寫法有很多變形,但無論是哪一種變形,都能保證線程安全,因為餓漢寫法是在類加載的時候,就完成了對象的初始化,類加載保證了他們天生是線程安全的。下面給出常見的2中餓漢寫法

public class HungrySingleton {
  private static final HungrySingleton singleton = new HungrySingleton();

  private HungrySingleton(){

  }

  public static HungrySingleton getSingleton(){
    return singleton;
  }
}
public class HungrySingleton {
  private static final HungrySingleton singleton = new HungrySingleton();

  private HungrySingleton(){

  }

//  public static HungrySingleton getSingleton(){
//    return singleton;
//  }
}

這兩種對初始化單例的對象上面,都是一致的, 通過final來保證對象的唯一。不同的是,調(diào)用單例對象的方式,第一種是通過getSingleton(),第二種是通過類.類變量的形式。

靜態(tài)內(nèi)部類實現(xiàn)單例模式

雙重檢查鎖(DCL)實現(xiàn)單例模式,雖然解決了線程不安全的問題,以及保證了資源的懶加載,在需要的時候,才會進(jìn)行實例化的操作。但是在某些情況下(比如JDK低于1.5)會出現(xiàn)DCL失效,所以有一種很簡潔且依舊是懶加載的方法實現(xiàn)單例模式。寫法如下:

public class StaticSingleton {

  private StaticSingleton(){
  }
  public static final StaticSingleton getInstance(){
    return Holder.singleton;
  }

  private static class Holder{
    private static final StaticSingleton singleton = new StaticSingleton();
  }
}

通過靜態(tài)內(nèi)部類的形式,實現(xiàn)單例類的初始化,其特性同樣是通過ClassLoader來保證其單例對象的唯一,但是這是懶加載的,因為只有在Holder類被調(diào)用的時候,即getInstance調(diào)用的時候,才會加載Holder類從而實現(xiàn)創(chuàng)建對象。

枚舉類實現(xiàn)單例模式

直接看代碼:

public enum EnumSingleton {
  SINGLETON;
  public void doSometings(){
    
  }
}

使用的時候,直接通過EnumSingleton.SINGLETON.doSomethings()。枚舉類天生特性是保證不會有兩個實例,并且只有在第一次訪問的時候才會被實例化,是懶加載的情況。

真的不會再次創(chuàng)建新的對象嗎?

在常規(guī)調(diào)用單例類的getInstance()方法的情況下,使用線程安全的寫法確實不會創(chuàng)建新的對象,但是Java提供了很多奇特的技巧和使用,下面這些使用會破壞掉常規(guī)的單例。

  • 反序列化
  • 反射
  • 克隆
  • 分布式環(huán)境下,多個類加載器

在除了枚舉實現(xiàn)單例模式的方法以外,其余所有方法碰到上述四種情況,都會重新創(chuàng)建對象。原因如下:

  • 反序列化會調(diào)用一個特殊的readResolve()方法來創(chuàng)建新的對象。我們可以重寫該方法,讓他返回原來的instance,而不是重新創(chuàng)建一個。
  • 反射會得到私有的構(gòu)造函數(shù),只能在構(gòu)造函數(shù)中加一個判斷,如果對象不為null,則扔出一個運行時異常,如果不這樣,只有枚舉能解決,因為枚舉自帶的特性。
  • 克隆,因為直接拷貝的內(nèi)存空間的內(nèi)容,所以只有自己重寫單例類的clone方法,如果不這樣,也只有枚舉能解決,因為枚舉沒有克隆方法。
  • 多分布式環(huán)境,因為我們上述很多種單例的寫法,都是依賴于類加載器的特性,但是static的作用只負(fù)責(zé)到類加載器,所以當(dāng)工程中存在多個類加載器的時候,就會創(chuàng)建多個實例,這種通常就需要第三方庫來解決。

什么時候用單例模式,用哪一種寫法的單例模式

單例模式有兩種比較適合的使用場景。
第一種是創(chuàng)建某個對象,需要的代價比較大,為了避免頻繁的創(chuàng)建和銷毀對象從而引起的對資源的浪費,會考慮使用單例模式。
第二種是這個對象必須只有一個,有多個會造成不可預(yù)估的錯誤,或者程序的混亂,比如只會有一個序號生成器,一個緩存等等。
針對使用的單例模式,如果需要理解的加載資源,就是用餓漢寫法,在Android應(yīng)用中,很多對象需要在啟動的時候,立即就使用,比如啟動時,需要拉取相機(jī)配置的類管理縮略圖的cache類等等。如果不是立即需要,或者不是貫穿應(yīng)用始終的,就不需要使用餓漢寫法,可以考慮懶漢寫法用(DCL或者靜態(tài)內(nèi)部類實現(xiàn))這兩種在一般情況下都不會出現(xiàn)問題。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Spring框架中@PostConstruct注解詳解

    Spring框架中@PostConstruct注解詳解

    在Spring項目經(jīng)常遇到@PostConstruct注解,下面這篇文章主要給大家介紹了關(guān)于Spring框架中@PostConstruct注解的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • IDEA 自定義方法注解模板的實現(xiàn)方法

    IDEA 自定義方法注解模板的實現(xiàn)方法

    這篇文章主要介紹了IDEA 自定義方法注解模板的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Java獲取上月份最后一天日期8位的示例代碼

    Java獲取上月份最后一天日期8位的示例代碼

    這篇文章主要介紹了Java獲取上月份最后一天日期8位,需要的朋友可以參考下
    2022-09-09
  • 使用easyexcel導(dǎo)出的excel文件,使用poi讀取時異常處理方案

    使用easyexcel導(dǎo)出的excel文件,使用poi讀取時異常處理方案

    這篇文章主要介紹了使用easyexcel導(dǎo)出的excel文件,使用poi讀取時異常處理方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 實例講解Java中的synchronized

    實例講解Java中的synchronized

    這篇文章主要介紹了Java中synchronized的使用方法,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • Java contains用法示例

    Java contains用法示例

    這篇文章主要介紹了Java contains的用法示例,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下
    2020-11-11
  • java實現(xiàn)單源最短路徑

    java實現(xiàn)單源最短路徑

    這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)單源最短路徑,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • springboot2中使用@JsonFormat注解不生效的解決

    springboot2中使用@JsonFormat注解不生效的解決

    這篇文章主要介紹了springboot2中使用@JsonFormat注解不生效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • 解決Spring在Thread中注入Bean無效的問題

    解決Spring在Thread中注入Bean無效的問題

    這篇文章主要介紹了解決Spring在Thread中注入Bean無效的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • 分享Spring Boot 3.x微服務(wù)升級歷程

    分享Spring Boot 3.x微服務(wù)升級歷程

    Spring Boot 3.0.0 GA版已經(jīng)發(fā)布,好多人也開始嘗試升級,有人測試升級后,啟動速度確實快了不少,這篇文章主要介紹了Spring Boot 3.x微服務(wù)升級經(jīng)歷,需要的朋友可以參考下
    2022-12-12

最新評論

阳信县| 红原县| 石泉县| 斗六市| 阿瓦提县| 永清县| 崇信县| 定远县| 旬邑县| 楚雄市| 山西省| 阿拉善盟| 呼伦贝尔市| 田阳县| 台湾省| 广饶县| 达孜县| 元朗区| 余姚市| 鄂托克旗| 若羌县| 辽宁省| 南丹县| 衡阳县| 余姚市| 黔西| 锦州市| 剑川县| 亚东县| 前郭尔| 任丘市| 长兴县| 海门市| 青海省| 独山县| 仪陇县| 阜城县| 南乐县| 桐庐县| 那坡县| 孟村|