亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

深入淺出單實例Singleton設計模式

2019-11-10 17:40:05
字體:
來源:轉載
供稿:網友

單實例Singleton設計模式可能是被討論和使用的最廣泛的一個設計模式了,這可能也是面試中問得最多的一個設計模式了。這個設計模式主要目的是想在整個系統中只能出現一個類的實例。這樣做當然是有必然的,比如你的軟件的全局配置信息,或者是一個Factory,或是一個主控類,等等。你希望這個類在整個系統中只能出現一個實例。當然,作為一個技術負責人的你,你當然有權利通過使用非技術的手段來達到你的目的。比如:你在團隊內部明文規定,“XX類只能有一個全局實例,如果某人使用兩次以上,那么該人將被處于2000元的罰款!”(呵呵),你當然有權這么做。但是如果你的設計的是東西是一個類庫,或是一個需要提供給用戶使用的API,恐怕你的這項規定將會失效。因為,你無權要求別人會那么做。所以,這就是為什么,我們希望通過使用技術的手段來達成這樣一個目的的原因。本文會帶著你深入整個Singleton的世界。

Singleton的教學版本

這里,我將直接給出一個Singleton的簡單實現,因為我相信你已經有這方面的一些基礎了。我們姑且把這個版本叫做1.0版

// version 1.0public class Singleton {    PRivate static Singleton singleton = null;    private Singleton() {  }    public static Singleton getInstance() {        if (singleton== null) {            singleton= new Singleton();        }        return singleton;    }}

在上面的實例中,我想說明下面幾個Singleton的特點:(下面這些東西可能是盡人皆知的,沒有什么新鮮的)

私有(private)的構造函數,表明這個類是不可能形成實例了。這主要是怕這個類會有多個實例。即然這個類是不可能形成實例,那么,我們需要一個靜態的方式讓其形成實例:getInstance()。注意這個方法是在new自己,因為其可以訪問私有的構造函數,所以他是可以保證實例被創建出來的。在getInstance()中,先做判斷是否已形成實例,如果已形成則直接返回,否則創建實例。所形成的實例保存在自己類中的私有成員中。我們取實例時,只需要使用Singleton.getInstance()就行了。

當然,如果你覺得知道了上面這些事情后就學成了,那得給你當頭棒喝一下了,事情遠遠沒有那么簡單。

Singleton的實際版本

上面的這個程序存在比較嚴重的問題,因為是全局性的實例,所以,在多線程情況下,所有的全局共享的東西都會變得非常的危險,這個也一樣,在多線程情況下,如果多個線程同時調用getInstance()的話,那么,可能會有多個進程同時通過 (singleton== null)的條件檢查,于是,多個實例就創建出來,并且很可能造成內存泄露問題。嗯,熟悉多線程的你一定會說——“我們需要線程互斥或同步”,沒錯,我們需要這個事情,于是我們的Singleton升級成1.1版,如下所示:

// version 1.1public class Singleton{    private static Singleton singleton = null;    private Singleton() {  }    public static Singleton getInstance() {        if (singleton== null) {            synchronized (Singleton.class) {                singleton= new Singleton();            }        }        return singleton;    }}嗯,使用了java的synchronized方法,看起來不錯哦。應該沒有問題了吧?!錯!這還是有問題!為什么呢?前面已經說過,如果有多個線程同時通過(singleton== null)的條件檢查(因為他們并行運行),雖然我們的synchronized方法會幫助我們同步所有的線程,讓我們并行線程變成串行的一個一個去new,那不還是一樣的嗎?同樣會出現很多實例。嗯,確實如此!看來,還得把那個判斷(singleton== null)條件也同步起來。于是,我們的Singleton再次升級成1.2版本,如下所示:// version 1.2public class Singleton{    private static Singleton singleton = null;    private Singleton()  {  }    public static Singleton getInstance()  {        synchronized (Singleton.class) {            if (singleton== null) {        singleton= new Singleton();            }         }        return singleton;    }}

不錯不錯,看似很不錯了。在多線程下應該沒有什么問題了,不是嗎?的確是這樣的,1.2版的Singleton在多線程下的確沒有問題了,因為我們同步了所有的線程。只不過嘛……,什么?!還不行?!是的,還是有點小問題,我們本來只是想讓new這個操作并行就可以了,現在,只要是進入getInstance()的線程都得同步啊,注意,創建對象的動作只有一次,后面的動作全是讀取那個成員變量,這些讀取的動作不需要線程同步啊。這樣的作法感覺非常極端啊,為了一個初始化的創建動作,居然讓我們達上了所有的讀操作,嚴重影響后續的性能?。?/p>

還得改!嗯,看來,在線程同步前還得加一個(singleton== null)的條件判斷,如果對象已經創建了,那么就不需要線程的同步了。OK,下面是1.3版的Singleton。

// version 1.3public class Singleton{    private static Singleton singleton = null;    private Singleton()  {    }    public static Singleton getInstance() {        if (singleton== null)  {            synchronized (Singleton.class) {                if (singleton== null)  {                    singleton= new Singleton();                }            }        }        return singleton;    }}

感覺代碼開始變得有點羅嗦和復雜了,不過,這可能是最不錯的一個版本了,這個版本又叫“雙重檢查”Double-Check。下面是說明:

第一個條件是說,如果實例創建了,那就不需要同步了,直接返回就好了。不然,我們就開始同步線程。第二個條件是說,如果被同步的線程中,有一個線程創建了對象,那么別的線程就不用再創建了。

相當不錯啊,干得非常漂亮!請大家為我們的1.3版起立鼓掌!

但是,如果你認為這個版本大攻告成,你就錯了。

主要在于singleton = new Singleton()這句,這并非是一個原子操作,事實上在 JVM 中這句話大概做了下面 3 件事情。

給 singleton 分配內存調用 Singleton 的構造函數來初始化成員變量,形成實例將singleton對象指向分配的內存空間(執行完這步 singleton才是非 null 了)

但是在 JVM 的即時編譯器中存在指令重排序的優化。也就是說上面的第二步和第三步的順序是不能保證的,最終的執行順序可能是 1-2-3 也可能是 1-3-2。如果是后者,則在 3 執行完畢、2 未執行之前,被線程二搶占了,這時 instance 已經是非 null 了(但卻沒有初始化),所以線程二會直接返回 instance,然后使用,然后順理成章地報錯。

對此,我們只需要把singleton聲明成 volatile 就可以了。下面是1.4版:

// version 1.4public class Singleton{    private volatile static Singleton singleton = null;    private Singleton()  {    }    public static Singleton getInstance()   {        if (singleton== null)  {            synchronized (Singleton.class) {                if (singleton== null)  {                    singleton= new Singleton();                }            }        }        return singleton;    }}

使用 volatile 有兩個功用:

1)這個變量不會在多個線程中存在復本,直接從內存讀取。

2)這個關鍵字會禁止指令重排序優化。也就是說,在 volatile 變量的賦值操作后面會有一個內存屏障(生成的匯編代碼上),讀操作不會被重排序到內存屏障之前。

但是,這個事情僅在Java 1.5版后有用,1.5版之前用這個變量也有問題,因為老版本的Java的內存模型是有缺陷的。

Singleton 的簡化版本

上面的玩法實在是太復雜了,一點也不優雅,下面是一種更為優雅的方式:

這種方法非常簡單,因為單例的實例被聲明成 static 和 final 變量了,在第一次加載類到內存中時就會初始化,所以創建實例本身是線程安全的。

// version 1.5public class Singleton{    private volatile static Singleton singleton = new Singleton();    private Singleton()  {    }    public static Singleton getInstance()   {        return singleton;    }}

但是,這種玩法的最大問題是——當這個類被加載的時候,new Singleton() 這句話就會被執行,就算是getInstance()沒有被調用,類也被初始化了。

于是,這個可能會與我們想要的行為不一樣,比如,我的類的構造函數中,有一些事可能需要依賴于別的類干的一些事(比如某個配置文件,或是某個被其它類創建的資源),我們希望他能在我第一次getInstance()時才被真正的創建。這樣,我們可以控制真正的類創建的時刻,而不是把類的創建委托給了類裝載器

好吧,我們還得繞一下:

下面的這個1.6版是老版《Effective Java》中推薦的方式。

// version 1.6public class Singleton {    private static class SingletonHolder {        private static final Singleton INSTANCE = new Singleton();    }    private Singleton (){}    public static final Singleton getInstance() {        return SingletonHolder.INSTANCE;    }}

上面這種方式,仍然使用JVM本身機制保證了線程安全問題;由于 SingletonHolder 是私有的,除了 getInstance() 之外沒有辦法訪問它,因此它只有在getInstance()被調用時才會真正創建;同時讀取實例的時候不會進行同步,沒有性能缺陷;也不依賴 JDK 版本。

Singleton 優雅版本

public enum Singleton{   INSTANCE;}

居然用枚舉?。】瓷先ズ门?#36924;,通過EasySingleton.INSTANCE來訪問,這比調用getInstance()方法簡單多了。

默認枚舉實例的創建是線程安全的,所以不需要擔心線程安全的問題。但是在枚舉中的其他任何方法的線程安全由程序員自己負責。還有防止上面的通過反射機制調用私用構造器。

這個版本基本上消除了絕大多數的問題。代碼也非常簡單,實在無法不用。這也是新版的《Effective Java》中推薦的模式。

Singleton的其它問題

怎么?還有問題?!當然還有,請記住下面這條規則——“無論你的代碼寫得有多好,其只能在特定的范圍內工作,超出這個范圍就要出Bug了”,這是“陳式第一定理”,呵呵。你能想一想還有什么情況會讓這個我們上面的代碼出問題嗎?

在C++下,我不是很好舉例,但是在Java的環境下,嘿嘿,還是讓我們來看看下面的一些反例和一些別的事情的討論(當然,有些反例可能屬于鉆牛角尖,可能有點學院派,不過也不排除其實際可能性,就算是提個醒吧):

其一、Class Loader。不知道你對Java的Class Loader熟悉嗎?“類裝載器”?!C++可沒有這個東西啊。這是Java動態性的核心。顧名思義,類裝載器是用來把類(class)裝載進JVM的。JVM規范定義了兩種類型的類裝載器:啟動內裝載器(bootstrap)和用戶自定義裝載器(user-defined class loader)。 在一個JVM中可能存在多個ClassLoader,每個ClassLoader擁有自己的NameSpace。一個ClassLoader只能擁有一個class對象類型的實例,但是不同的ClassLoader可能擁有相同的class對象實例,這時可能產生致命的問題。如ClassLoaderA,裝載了類A的類型實例A1,而ClassLoaderB,也裝載了類A的對象實例A2。邏輯上講A1=A2,但是由于A1和A2來自于不同的ClassLoader,它們實際上是完全不同的,如果A中定義了一個靜態變量c,則c在不同的ClassLoader中的值是不同的。

于是,如果咱們的Singleton 1.3版本如果面對著多個Class Loader會怎么樣?呵呵,多個實例同樣會被多個Class Loader創建出來,當然,這個有點牽強,不過他確實存在。難道我們還要整出個1.4版嗎?可是,我們怎么可能在我的Singleton類中操作Class Loader啊?是的,你根本不可能。在這種情況下,你能做的只有是——“保證多個Class Loader不會裝載同一個Singleton”。

其二、序例化。如果我們的這個Singleton類是一個關于我們程序配置信息的類。我們需要它有序列化的功能,那么,當反序列化的時候,我們將無法控制別人不多次反序列化。不過,我們可以利用一下Serializable接口的readResolve()方法,比如:

public class Singleton implements Serializable{    ......    ......    protected Object readResolve()    {        return getInstance();    }}

其三、多個Java虛擬機。如果我們的程序運行在多個Java的虛擬機中。什么?多個虛擬機?這是一種什么樣的情況啊。嗯,這種情況是有點極端,不過還是可能出現,比如EJB或RMI之流的東西。要在這種環境下避免多實例,看來只能通過良好的設計或非技術來解決了。

其四,volatile變量。關于volatile這個關鍵字所聲明的變量可以被看作是一種 “程度較輕的同步synchronized”;與 synchronized 塊相比,volatile 變量所需的編碼較少,并且運行時開銷也較少,但是它所能實現的功能也僅是synchronized的一部分。當然,如前面所述,我們需要的Singleton只是在創建的時候線程同步,而后面的讀取則不需要同步。所以,volatile變量并不能幫助我們即能解決問題,又有好的性能。而且,這種變量只能在JDK 1.5+版后才能使用。

其五、關于繼承。是的,繼承于Singleton后的子類也有可能造成多實例的問題。不過,因為我們早把Singleton的構造函數聲明成了私有的,所以也就杜絕了繼承這種事情。

其六,關于代碼重用。也話我們的系統中有很多個類需要用到這個模式,如果我們在每一個類都中有這樣的代碼,那么就顯得有點傻了。那么,我們是否可以使用一種方法,把這具模式抽象出去?在C++下這是很容易的,因為有模板和友元,還支持棧上分配內存,所以比較容易一些(程序如下所示),Java下可能比較復雜一些,聰明的你知道怎么做嗎?

template class Singleton{    public:        static T& Instance()        {            static T theSingleInstance; //假設T有一個protected默認構造函數            return theSingleInstance;        }}; class OnlyOne : public Singleton{    friend class Singleton;    int example_data;     public:        int GetExampleData() const {return example_data;}    protected:        OnlyOne(): example_data(42) {}   // 默認構造函數        OnlyOne(OnlyOne&) {}}; int main( ){    cout << OnlyOne::Instance().GetExampleData() << endl;    return 0;}
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
色婷婷综合久久久久中文字幕1| 91chinesevideo永久地址| 亚洲人成电影网站色xx| 日韩欧美在线第一页| 97热在线精品视频在线观看| 久久精品视频免费播放| 欧美国产亚洲精品久久久8v| 96sao精品视频在线观看| 一区二区三区久久精品| 国产精品免费久久久| 欧美xxxwww| 久久久久久久久爱| 亚洲欧美国产另类| 自拍偷拍亚洲区| 欧美韩国理论所午夜片917电影| 播播国产欧美激情| 欧美精品九九久久| 午夜精品视频网站| 国产一区二区三区在线看| 激情懂色av一区av二区av| 日韩在线中文字幕| 久久五月情影视| 亚洲欧美另类在线观看| 亚洲爱爱爱爱爱| 日韩av电影院| 日韩电影免费观看中文字幕| 精品国偷自产在线视频| 欧洲s码亚洲m码精品一区| 亚洲精品456在线播放狼人| 欧美日韩在线免费| 国产精品免费网站| 欧美日本高清视频| 欧美日韩亚洲国产一区| 亚洲аv电影天堂网| 91精品一区二区| 国产精品亚洲欧美导航| 国产精品最新在线观看| 成人日韩av在线| 成人女保姆的销魂服务| 精品国产一区二区三区在线观看| 欧美视频在线免费| 亚洲天堂网在线观看| 精品一区电影国产| www高清在线视频日韩欧美| 精品久久久久久久久久久久久| 欧美精品午夜视频| 亚洲欧美综合另类中字| 国产精品网红福利| 亚州av一区二区| 欧美人在线观看| 色天天综合狠狠色| 国产精品18久久久久久麻辣| 亚洲午夜精品久久久久久性色| 欧美成aaa人片免费看| 欧美成人免费一级人片100| 国产精品精品久久久| 亚洲精选在线观看| 中文字幕亚洲第一| 久久国产精品首页| 国产自摸综合网| 欧美成人一区在线| 国内揄拍国内精品| 一本一本久久a久久精品牛牛影视| 亚洲肉体裸体xxxx137| 中日韩午夜理伦电影免费| 久久久视频免费观看| 日韩精品在线免费观看视频| 色播久久人人爽人人爽人人片视av| 欧美午夜女人视频在线| 91高清视频免费| 91精品啪在线观看麻豆免费| 中文字幕亚洲综合久久筱田步美| 亚洲欧美日韩视频一区| 中文字幕在线看视频国产欧美| 成人激情在线观看| 欧美乱妇40p| 欧美激情小视频| 日韩在线国产精品| 中文字幕精品视频| 欧美黄网免费在线观看| 国产精品视频久| 欧美日韩亚洲一区二| 欧美日韩亚洲激情| 91国内产香蕉| 91热精品视频| 日韩av免费观影| 亚洲精品电影在线| 欧美与欧洲交xxxx免费观看| 久久久91精品国产| 日产日韩在线亚洲欧美| 欧美成人免费网| 精品国产精品自拍| 欧美日韩国产成人在线| 98午夜经典影视| 一区二区三区四区视频| 国产成人啪精品视频免费网| 亚洲人精选亚洲人成在线| 色一区av在线| 日韩美女在线观看一区| 成人黄色免费在线观看| 国产精品电影网站| 中国日韩欧美久久久久久久久| 久久亚洲精品视频| 在线视频免费一区二区| 性日韩欧美在线视频| 久久久久久久一区二区三区| 久久国产精品久久久久久| 91色视频在线观看| 日韩电影免费观看在线观看| 国产精品久久久久久久9999| 欧美极品少妇与黑人| 亚洲色图17p| 欧美激情视频在线免费观看 欧美视频免费一| 日本乱人伦a精品| 日本中文字幕成人| 海角国产乱辈乱精品视频| 狠狠躁天天躁日日躁欧美| 日韩电影在线观看中文字幕| 欧美亚洲另类在线| 亚洲免费成人av电影| 日韩欧美一区二区三区久久| 国产成人免费91av在线| 久久电影一区二区| 国产精品亚洲网站| 久久久久久18| 中文字幕亚洲精品| 亚洲国产精品99久久| 亚洲欧美精品伊人久久| 国产精品高潮呻吟视频| 国产男人精品视频| 国产在线98福利播放视频| 久久亚洲影音av资源网| 色噜噜狠狠狠综合曰曰曰| 国产伦精品一区二区三区精品视频| 亚洲欧美日韩一区二区在线| 欧美一区二三区| 欧美日韩另类字幕中文| 欧美一级大片在线观看| 成人中文字幕+乱码+中文字幕| 亚洲韩国日本中文字幕| 日本高清不卡的在线| 久久精品国产久精国产思思| 成人午夜高潮视频| 成人h猎奇视频网站| 亚洲精品不卡在线| 国产日韩欧美电影在线观看| 日本精品视频在线播放| 国产精品一区二区久久| 欧美在线亚洲一区| 国产精品亚洲欧美导航| 91经典在线视频| 欧美电影免费观看电视剧大全| 日韩av在线免费播放| 亚洲欧洲在线观看| 色悠悠国产精品| 国产精品自产拍在线观| 国产精品美女无圣光视频| 色一区av在线| 欧美一级视频在线观看| 精品国产欧美成人夜夜嗨| 亚洲九九九在线观看| 国产成人精品综合| 色偷偷88888欧美精品久久久| 亚洲第一精品自拍|