單例模式的實現

洞拐洞拐發表於2016-08-27

  單例模式就是是一個類僅可以建立一個物件,在java中實現主要有兩種方式:餓漢式和懶漢式。

  先看兩種方式共有的部分,因為是單例,所以構造方法必須是私有的private,而且必須提供一個對外界開放的獲取物件的方法,該方法內部控制返回唯一的一個物件例項:

public class Singleton {

    //構造方法私有,阻斷外界直接建立物件的方法
    private Singleton() {}
    
    //提供一個獲取唯一物件例項的方法
    public static Singleton getInstance(){
        //...
    }
    
}

  以上是不管什麼方式的實現,都得遵循的一些規定,下面就介紹懶漢式和餓漢式:

  餓漢式是單例類感覺自己很飢餓(將例項物件想象為吃的),不管有沒有別的類跟我要例項類,我都要自己先生成一個,像下面的實現: 

public class Singleton {

    //構造方法私有,阻斷外界直接建立物件的方法
    private Singleton() {}
    
    //內部持有一個類初始化時唯一建立的一個類例項物件
    private static Singleton singleton = new Singleton();
    
    //提供一個獲取唯一物件例項的方法
    public static Singleton getInstance(){
        return singleton;
    }
    
}

  餓漢式的有點在於編碼邏輯簡單好理解,無執行緒安全問題;缺點嘛,自然就是當我僅僅是想用Singleton類其他方法,他還是建立了一個對我現在來說沒用的例項物件,當系統中這種餓漢式的單例類多起來的時候,無疑是一種資源浪費,這個問題正好懶漢式可以解決。

  懶漢式,就是自己首先持有一個空的單例類的例項,但是不會類一載入就建立例項,只有當別人第一次要我的例項物件我才給他建立,懶嘛,要是沒人要我就不建立了:

public class Singleton {

    //構造方法私有,阻斷外界直接建立物件的方法
    private Singleton() {}
    
    //內部持有一個單例類的引用
    private static Singleton singleton = null;
    
    //提供一個獲取唯一物件例項的方法
    public static Singleton getInstance(){
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }
    
}

  餓漢式的有點不用說了,就是解決了懶漢式的缺點;但是他的缺點就是多執行緒環境下,不盡人意啊,比如兩個執行緒同時都是第一次去獲取Singleton類的例項的時候,又同時執行到if(singleton==null)這一行,兩個執行緒就會同時進入if語句執行體中去,可能先後執行了singleton = new Singleton();這就違反了單例模式的概念,所以還得想個辦法解決這個問題。

  執行緒安全的餓漢式:(其實只要將if語句上加上執行緒鎖,就可以避免兩個執行緒一起跑到這個地方來了)

public class Singleton {

    //構造方法私有,阻斷外界直接建立物件的方法
    private Singleton() {}
    
    //內部持有一個單例類的引用
    private static Singleton singleton = null;
    
    //提供一個獲取唯一物件例項的方法
    public static Singleton getInstance(){
        synchronized (Singleton.class) {
            if(singleton==null){
                singleton = new Singleton();
            }
        }
        return singleton;
    }
    
}

  踏噠~,解決了,但是!你發現了麼?這樣的話,每次別人想獲取sinleton例項的時候都得等待別的執行緒釋放鎖,自己再加鎖,自己再釋放鎖,而這些鎖的操作又是那麼消耗時間,能不能再優化一下。想一想,是不是第一次訪問完了,只要sington物件例項,不為空,直接返回就是了,沒有現成問題啊,應該這樣:

public class Singleton {

    //構造方法私有,阻斷外界直接建立物件的方法
    private Singleton() {}
    
    //內部持有一個單例類的引用
    private static Singleton singleton = null;
    
    //提供一個獲取唯一物件例項的方法
    public static Singleton getInstance(){
        if(singleton==null){
            synchronized (Singleton.class) {
                if(singleton==null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
    
}

  完美!執行緒安全的懶漢式,既解決了資源浪費問題,又兼顧了執行緒安全問題。

  不過還有個更巧妙地方法,這個就涉及到內部類的只是,當一個類載入的時候,其內部類並不載入,而是隻要第一次用到內部類的時候內部類才會載入,而且如果這個內部類是static內部類,也就是說載入了這個類一次,以後就直接獲取他就可以了,詳細參見另一篇部落格:http://www.cnblogs.com/WreckBear/p/5812942.html

public class Singleton {

    //構造方法私有,阻斷外界直接建立物件的方法
    private Singleton() {}
    
    /*
     * 搭建一個內部靜態類,外部類載入的時候,內部類並不會載入,
     * 只有當內部類被訪問的時候才會被載入、初始化,載入之後就會一直儲存在記憶體中
     */
    public static class Get{
        public static Singleton singleton = new Singleton();
    }
    
}

  在Main中獲取:

public class Main {

    public static void main(String[] args) {
        Singleton singleton = Singleton.Get.singleton;
    }
}

  這種方法顯得更加優雅一點,至此,結束!

相關文章