設計模式之單例模式

一、單態定義:

Singleton 模式主要作用是保證在 Java 應用程序中,一個類 Class 只有一個實例存在。

二. 單例模式的特點

1. 單例模式只能有一個實例。

2. 單例類必須創建自己的唯一實例。

3. 單例類必須向其他對象提供這一實例。

在很多操作中,比如建立目錄數據庫連接都需要這樣的單線程操作。

還有, singleton 能夠被狀態化; 這樣,多個單態類在一起就可以作為一個狀態倉庫一樣向外提供服務,比如,你要論壇中的帖子計數器,每次瀏覽一次需要計數,單態類能否保持住這個計數,並且能 synchronize 的安全自動加 1,如果你要把這個數字永久保存到數據庫,你可以在不修改單態接口的情況下方便的做到。

另外方面,Singleton 也能夠被無狀態化。提供工具性質的功能,Singleton 模式就為我們提供了這樣實現的可能。使用 Singleton 的好處還在於可以節省內存,因為它限制了實例的個數,有利於 Java 垃圾回收(garbage collection)。我們常常看到工廠模式中類裝入器(class loader)中也用 Singleton 模式實現的,因為被裝入的類實際也屬於資源。

如何使用?

第一種形式:

public class Singleton {

private Singleton(){}

//注意這是 private 只供內部調用

private static Singleton instance = new Singleton();

//這裡提供了一個供外部訪問本 class 的靜態方法,可以直接訪問

public static Singleton getInstance() {

return instance;

}

}

第二種形式:

public class Singleton {

private static Singleton instance = null;

public static synchronized Singleton getInstance() {

//這個方法比上面有所改進,不用每次都進行生成對象,只是第一次

//使用時生成實例,提高了效率!

if (instance==null)

instance=new Singleton();

return instance; }

}

使用 Singleton.getInstance()可以訪問單態類。

第三種形式:

public class SingletonDemo {

private static SingletonDemo instance;

private SingletonDemo(){

}

public static synchronized SingletonDemo getInstance(){

if(instance==null){

instance=new SingletonDemo();

}

return instance;

}

}

第三種形式中的 synchronized,這個 synchronized 很重要,如果沒有 synchronized,一個是考慮了線程安全,再就是如果不加的話 getInstance()是有可能得到多個 Singleton 實例。

第四種形式:

public class SingletonDemo {

private static class SingletonHolder{

private static SingletonDemo instance=new SingletonDemo();

}

private SingletonDemo(){

System.out.println("Singleton has loaded");

}

public static SingletonDemo getInstance(){

return SingletonHolder.instance;

}

}

第四種形式使用內部類的好處是,靜態內部類不會在單例加載時就加載,而是在調用getInstance()方法時才進行加載,達到了類似懶漢模式的效果,而這種方法又是線程安全的。

第五種形式:

public class SingletonDemo {

private volatile static SingletonDemo instance;

private SingletonDemo(){

System.out.println("Singleton has loaded");

}

public static SingletonDemo getInstance(){

if(instance==null){

synchronized (SingletonDemo.class){

if(instance==null){

instance=new SingletonDemo();

}

}

}

return instance;

}

}

第五種形式叫雙重校驗鎖法,下面是這種形式的使用場景。

STEP 1. 線程A訪問getInstance()方法,因為單例還沒有實例化,所以進入了鎖定塊。

STEP 2. 線程B訪問getInstance()方法,因為單例還沒有實例化,得以訪問接下來代碼塊,而接下來代碼塊已經被線程1鎖定。

STEP 3. 線程A進入下一判斷,因為單例還沒有實例化,所以進行單例實例化,成功實例化後退出代碼塊,解除鎖定。

STEP 4. 線程B進入接下來代碼塊,鎖定線程,進入下一判斷,因為已經實例化,退出代碼塊,解除鎖定。

STEP 5. 線程A獲取到了單例實例並返回,線程B沒有獲取到單例並返回Null。

理論上雙重校驗鎖法是線程安全的

Singleton 模式看起來簡單,使用方法也很方便,但是真正用好,是非常不容易,需要對 Java 的類 線程 內存等概念有相當的瞭解。

總之:如果你的應用基於容器,那麼 Singleton 模式少用或者不用,可以使用相關替代技術。


分享到:


相關文章: