免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
java常見設(shè)計(jì)模式之---單例模式

java常見設(shè)計(jì)模式之---單例模式

1、單例模式簡(jiǎn)介

單例模式(Singleton Pattern)是一個(gè)比較簡(jiǎn)單的模式,其原始定義如下:Ensure a class has only one instance, and provide a global point of access to it. 即確保只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。

因程序需要,有時(shí)我們只需要某個(gè)類只保留一個(gè)對(duì)象,不希望有更多對(duì)象,此時(shí),我們則應(yīng)考慮單例模式的設(shè)計(jì)。在計(jì)算機(jī)系統(tǒng)中,線程池、緩存、日志對(duì)象、對(duì)話框、打印機(jī)、顯卡的驅(qū)動(dòng)程序?qū)ο蟪1辉O(shè)計(jì)成單例。事實(shí)上,這些應(yīng)用都或多或少具有資源管理器的功能。

總之,選擇單例模式就是為了避免不一致狀態(tài)。

應(yīng)用場(chǎng)景舉例

1)、spring中的單例模式:在spring中每個(gè)bean都是單例的,這樣做的好處是Spring容器可以管理這些bean的生命周期,由spring容器來決定實(shí)例的創(chuàng)建、銷毀、銷毀后的處理等等問題。如果采用非單例模式,則Bean初始化后的管理交給J2EE容器了,Spring容器就不在跟蹤管理Bean的生命周期了。

2)、多線程的線程池的設(shè)計(jì)一般也是采用單例模式,這是由于線程池要方便對(duì)池中的線程進(jìn)行控制。。

3)、操作系統(tǒng)的文件系統(tǒng),也是大的單例模式實(shí)現(xiàn)的具體例子,一個(gè)操作系統(tǒng)只能有一個(gè)文件系統(tǒng)。

4)、應(yīng)用程序的日志應(yīng)用,一般都何用單例模式實(shí)現(xiàn),這一般是由于共享的日志文件一直處于打開狀態(tài),因?yàn)橹荒苡幸粋€(gè)實(shí)例去操作,否則內(nèi)容不好追加

5)、Web應(yīng)用的配置對(duì)象的讀取,一般也應(yīng)用單例模式,這個(gè)是由于配置文件是共享的資源

2、單例模式的特點(diǎn)

包括下面幾個(gè)部分的簡(jiǎn)單記憶”3125

3和1一起記憶,即三個(gè)一

  • 單例模式只能有一個(gè)實(shí)例,
  • 單例類必須創(chuàng)建自己的唯一實(shí)例
  • 單例類必須向其他對(duì)象提供這一實(shí)例

綜上所述,單例模式就是為確保一個(gè)類只有一個(gè)實(shí)例,并為整個(gè)系統(tǒng)提供一個(gè)全局訪問點(diǎn)的一種方法。

2即是指2種實(shí)現(xiàn)方式:懶漢式和 餓漢式
5即是指五種經(jīng)典實(shí)現(xiàn)

有兩種場(chǎng)景可能導(dǎo)致非單例的情況

場(chǎng)景一:如果單例由不同的類加載器加載,那便有可能存在多個(gè)單例類的實(shí)例

場(chǎng)景二:如果 Singleton 實(shí)現(xiàn)了 java.io.Serializable 接口,那么這個(gè)類的實(shí)例就可能被序列化和反序列化。

3、單例模式和靜態(tài)類

靜態(tài)類也可以實(shí)現(xiàn)一個(gè)類只有一個(gè)對(duì)象,這又和單例模式有什么區(qū)別呢?

  1. 單例可以繼承和被繼承,方法可以被重寫(override),但是靜態(tài)方法不行。
  2. 靜態(tài)方法中產(chǎn)生的對(duì)象會(huì)在執(zhí)行后被釋放,進(jìn)而被GC所清理,不會(huì)一直存在于內(nèi)存中。
  3. 靜態(tài)類會(huì)在第一次運(yùn)行時(shí)初始化(餓漢式也這樣),單例模式可以有其他選擇,如立即加載(餓漢式)和延遲加載(懶漢式)
  4. 基于上面兩條,單例模式往往存在于DAO層,如果反復(fù)的初始化和釋放會(huì)占用很多系統(tǒng)資源,而使用單例模式將其加載于內(nèi)存中可以節(jié)省資源開銷。
  5. 單例模式容易測(cè)試

立即加載 : 在類加載初始化的時(shí)候就主動(dòng)創(chuàng)建實(shí)例;
延遲加載 : 等到真正使用的時(shí)候才去創(chuàng)建實(shí)例,不用時(shí)不去主動(dòng)創(chuàng)建。

靜態(tài)類和單例模式情景的選擇:

情景一:不需要維持任何狀態(tài),僅僅用于全局訪問,此時(shí)更適合使用靜態(tài)類。

情景二:需要維持一些特定的狀態(tài),此時(shí)更適合使用單例模式。

4、單例模式的經(jīng)典實(shí)現(xiàn)

餓漢式單例(典型實(shí)現(xiàn))

想想“三個(gè)一”特點(diǎn)
餓漢式單例實(shí)現(xiàn)的方式如下,這種方法是線程安全的,因?yàn)閱卫惐患虞d時(shí),就會(huì)實(shí)例化一個(gè)對(duì)象并交給自己的引用,供系統(tǒng)使用;而且,由于這個(gè)類在整個(gè)生命周期中只會(huì)被加載一次,因此只會(huì)創(chuàng)建一個(gè)實(shí)例,即能夠充分保證單例。

//餓漢式單例實(shí)現(xiàn)public class Singleton1 {    private Singleton1(){}    private static Singleton1 singleton1 = new Singleton1();    public static Singleton1 getInstance (){        return singleton1;    }}

**缺點(diǎn):**因?yàn)槭橇⒓醇虞d的方式創(chuàng)建單例,所以在單例無用的情況下回造成資源的浪費(fèi),大量餓漢式單例會(huì)給系統(tǒng)造成極大的開銷。

餓漢式-靜態(tài)代碼塊

public class Singleton {   private Singleton instance = null;   private Singleton() {}// 初始化順序:基靜態(tài)、子靜態(tài) -> 基實(shí)例代碼塊、基構(gòu)造 -> 子實(shí)例代碼塊、子構(gòu)造   static {       instance = new Singleton();  }   public static Singleton getInstance() {       return this.instance;  }}

類初始化時(shí)實(shí)例化 instance

懶漢式單例創(chuàng)建,五種方法

想想“3125”中的二和五

0)、懶漢式(典型實(shí)現(xiàn))

//不完美的餓漢式單例public class Singleton2 {    private Singleton2(){}    private static Singleton2 instance = null;    public static Singleton2 getInstance (){        if (instance == null){            instance = new Singleton2();        }        return instance ;    }}

為什么說上面的餓漢式單例不完美呢,在單線程情況下,這種方法是線程安全的,但假設(shè)在多線程情況下
多個(gè)線程會(huì)同時(shí)執(zhí)行到if (instance == null),此時(shí)尚未有線程創(chuàng)建出instance = new Singleton2();,這時(shí)就會(huì)有多個(gè)線程進(jìn)入到if判斷語(yǔ)句中,創(chuàng)建出多個(gè)對(duì)象。

這里介紹個(gè)知識(shí)點(diǎn),在下面也有用到:

當(dāng)我們寫了 new 操作,JVM 到底會(huì)發(fā)生什么?
首先,我們要明白的是,new Singleton()是一個(gè)非原子操作,代碼行instance = new Singleton2();的執(zhí)行過程可以分為下面三步:
(1)、memory = allocate(); //先在內(nèi)存開辟空間
(2)、ctorInstance(memory); //初始化對(duì)象
(3)、instance = memory; //使instance 指向剛分配的內(nèi)存地址(執(zhí)行完這步 instance 才是非Null的)

要留意的是:這個(gè)過程可能發(fā)生無序?qū)懭?指令重排序),也就是說上面的3行指令可能會(huì)被重排序?qū)е孪葓?zhí)行第3行后執(zhí)行第2行,也就是說其真實(shí)執(zhí)行順序可能是下面這種:
(1)、memory = allocate(); //先在內(nèi)存開辟空間
(2)、instance = memory; //使instance 指向剛分配的內(nèi)存地址
(3)、ctorInstance(memory); //初始化對(duì)象

1)、同步延遲加載 — synchronized方法

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

這種方法確實(shí)做到了線程安全,但是在每一個(gè)線程執(zhí)行到getInstance()方法時(shí),只有一個(gè)線程獲得鎖,其他線程需要等待。這樣就會(huì)因?yàn)榈却i資源造成系統(tǒng)性能的下降。

上面?zhèn)鹘y(tǒng)懶漢式單例的實(shí)現(xiàn)唯一的差別就在于:是否使用 synchronized 修飾 getSingleton3()方法。若使用,就保證了對(duì)臨界資源的同步互斥訪問,也就保證了單例。

2)、雙重檢驗(yàn)鎖模式(double checked locking pattern)

對(duì)上面的方法進(jìn)行改進(jìn)

進(jìn)行兩次 instance == null? 的判斷,如果為true才能進(jìn)入同步代碼塊,而在同步代碼塊中再檢測(cè)一次是因?yàn)榭赡軙?huì)有多個(gè)線程一起進(jìn)入同步塊外的 if,如果在同步塊內(nèi)不進(jìn)行二次檢驗(yàn)的話就有可能會(huì)生成多個(gè)實(shí)例了。

//同步延遲加載 — synchronized塊public class Singleton4 {    private Singleton4(){}    private static Singleton4 instance= null;    public static Singleton4 getInstance(){    if (instance == null){        // 使用 synchronized 塊,臨界資源的同步互斥訪問        synchronized (Singleton4.class){                if (instance == null){                    instance = new Singleton4();                }            }        }        return instance;    }}

為什么說使用synchronized塊是線程不安全的呢,這里就需要用到前面提到的前置知識(shí)了。

如果intence = new Singleton4();的執(zhí)行順序是1-2-3
即:先分配內(nèi)存空間,再初始化成員變量,最后分配引用地址。這樣出來的instance 是非null的。

但如果執(zhí)行順序是1-3-2的情況下
即:先分配內(nèi)存空間,在分配引用地址,最后再初始化變量。

如果是后者,則在 3 執(zhí)行完畢、2 未執(zhí)行之前,此時(shí)另外有一個(gè)線程進(jìn)行第一個(gè)判斷,發(fā)現(xiàn)instance!=null,返回instance(但其實(shí)instance還沒有完成初始化),使用會(huì)導(dǎo)致報(bào)錯(cuò),當(dāng)然發(fā)生錯(cuò)誤的概率極低,但是這個(gè)錯(cuò)誤我們是可以規(guī)避的。

只需要將 instance 變量聲明成 volatile 就可以了。

雙重檢驗(yàn)鎖-volatile關(guān)鍵字防止重排序

public class Singleton4 {    private Singleton4(){}    //給instance加上了volatile關(guān)鍵字    private volatile static Singleton4 instance= null;    public static Singleton4 getInstance(){            if (instance == null){            // 使用 synchronized 塊,臨界資源的同步互斥訪問            synchronized (Singleton4.class){                if (instance == null){                    instance = new Singleton4();                }            }        }        return instance;    }}

使用 volatile 的主要原因是其另一個(gè)特性:禁止指令重排序優(yōu)化。也就是說,在 volatile 變量的賦值操作后面會(huì)有一個(gè)內(nèi)存屏障(生成的匯編代碼上),讀操作不會(huì)被重排序到內(nèi)存屏障之前。比如上面的例子,取操作必須在執(zhí)行完 1-2-3 之后或者 1-3-2 之后,不存在執(zhí)行到 1-3 然后取到值的情況。從「先行發(fā)生原則」的角度理解的話,就是對(duì)于一個(gè) volatile 變量的寫操作都先行發(fā)生于后面對(duì)這個(gè)變量的讀操作(這里的“后面”是時(shí)間上的先后順序)。

但是特別注意
在 Java 5 以前的版本使用了 volatile 的雙檢鎖還是有問題的。其原因是 Java 5 以前的 JMM (Java 內(nèi)存模型)是存在缺陷的,即時(shí)將變量聲明成 volatile 也不能完全避免重排序,主要是 volatile 變量前后的代碼仍然存在重排序問題。這個(gè) volatile 屏蔽重排序的問題在 Java 5 中才得以修復(fù),所以在這之后才可以放心使用 volatile。

3)、靜態(tài)內(nèi)部類

public class Singleton {  //靜態(tài)內(nèi)部類里面創(chuàng)建了一個(gè)Singleton單例   private static class InstanceHolder {        private static final Singleton INSTANCE = new Singleton();    }     private Singleton (){}     public static final Singleton getInstance() {        return InstanceHolder.INSTANCE;    }  }  

這種寫法仍然使用JVM本身機(jī)制保證了線程安全問題;由于 SingletonHolder 是私有的,除了 getInstance() 之外沒有辦法訪問它,因此它是懶漢式的;同時(shí)讀取實(shí)例的時(shí)候不會(huì)進(jìn)行同步,沒有性能缺陷;也不依賴 JDK 版本。

4)、lock機(jī)制

// 類似雙重校驗(yàn)鎖寫法public class Singleton {      private static Singleton instance = null;      private static Lock lock = new ReentrantLock();      private Singleton() {}      public static Singleton getInstance() {          if(instance == null) {              lock.lock(); // 顯式調(diào)用,手動(dòng)加鎖              if(instance == null) {                  instance = new Singleton();              }              lock.unlock(); // 顯式調(diào)用,手動(dòng)解鎖          }          return instance;      }}

5)、枚舉法

public enum singleton5 {INSTANCE;private  InnerClass _instance;private singleton5() {_instance = new InnerClass();}public InnerClass getInstance() {return _instance;}private class InnerClass{}}

主要是枚舉類型是值類型的數(shù)據(jù),所以不可能獲取到構(gòu)造函數(shù),也就無法通過構(gòu)造函數(shù)來得到新的實(shí)例。這樣可以實(shí)現(xiàn)很安全的單例模式。(目前很多使用枚舉實(shí)現(xiàn)單例模式)

缺點(diǎn):
枚舉類型會(huì)造成更多的內(nèi)存消耗。枚舉會(huì)比使用靜態(tài)變量多消耗兩倍的內(nèi)存,如果是Android應(yīng)用,盡量避免。

特點(diǎn):
(1)線程安全(枚舉類型默認(rèn)就是安全的)

(2)避免反序列化破壞單例

來源:https://www.icode9.com/content-1-801901.html
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Java設(shè)計(jì)模式-之Singleton單例模式 - 設(shè)計(jì)模式
Spring設(shè)計(jì)模式——單例模式
怎樣實(shí)現(xiàn)線程安全的延遲初始化單例模式 ??
Java單例模式深入詳解
設(shè)計(jì)模式——單例模式(Singleton)
(轉(zhuǎn))C#單例模式詳解
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服