Design Pattern: Singleton 模式

來源:互聯網
上載者:User

http://www.35java.com/zhibo/forum.php?mod=viewthread&tid=267&extra=page%3D2

Singleton的英文意義是獨身,也就是只有一個人,應用在物件導向語言上,通常翻譯作單例:單一個執行個體(Instance)。

很多時候,您會需要Singleton模式,例如印表機管理,您希望程式中只能有一個PrintSpooler,以避免兩個列印動作同時輸入至印表機中;例如資料庫管理,因為建立串連(Connection)物件會耗用資源,您希望程式中只能有一個串連物件,所有其它的程式都透過這個物件來串連資料庫,以避免串連物件的重複開啟造成資源的耗用;例如系統程式屬性檔的讀取,您使用單一個物件來讀取屬性內容,而程式的其它部份都向這個物件要求屬性資料,而不是自行讀取屬性資料。

以印表機設計為例,有的設計人員會採取全域變數的方式來建立執行個體,並在程式中隨機取用這個執行個體,Java雖然不支援全域變數,但透過將物件封裝在一個類別之中,也有人會採用這樣的寫法:
public class PrintSpooler {
    public PrintSpooler() {
        // ....
    }

    public Connection getSpooler(){
       ....
    }
}

public class GlobalObject {
    private PrintSpooler printSpooler;
    public GlobalObject () {
        printSpooler = new PrintSpooler();
        ...
    }

    public void getPrintSpooler() {
        return printSpooler;
    }
}

無論全域變數或是以上的例子,都無法保證只產生唯一個執行個體,您也許會注意不犯這個錯誤,但與您共同工作的夥伴也許會直覺的使用建構方法來產生一個 PrintSpooler執行個體。

Singleton模式可以保證一個類別只有一個執行個體,並提供一個訪問(visit)這個執行個體的方法。

一個Singleton實作即為Java中的java.lang.Runtime類別,每個Java程式執行時都有一個唯一的Runtime物件,可以透過它提供的靜態方法getRuntime()方法來取得這個物件,例如:
Runtime runtime = Runtime.getRuntime();

取得Runtime物件之後,您可以透過它進行一些外部命令的執行、進行垃圾處理等等指令,您可以開啟Runtime.java類別,開頭的幾行是這樣寫的:
public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    public static Runtime getRuntime() {
        return currentRuntime;
    }

   /** Don't let anyone else instantiate this class */
   private Runtime() {}

    // 以下略
}

上面結構即採用Singleton模式設計,其結構使用 UML 來表即如下所示:

如上所示的,Java使用 靜態工廠 來取得Runtime物件,其中Runtime的建構函式被宣告為private,這樣可以阻止其他人使用建構方法來建立執行個體;使用更一般化的表示單例的UML結構,如所示:

有幾個實作上面結構的方法,可以在第一次需要執行個體時再建立物件,也就是採用所謂的Lazy Initialization:
public class Singleton {
    private static Singleton instance = null;

    private Singleton() {
        // ....
    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }

        return instance;
    }

    // .. 其它實作
}

上面的實作適用於單執行緒的程式,在多執行緒的程式下,以下的寫法在多個執行緒的競爭資源下,將仍有可能產生兩個以上的執行個體,例如下面的情況:
Thread1: if(instance == null) // true
Thread2: if(instance == null) // true

Thread1: instance = new Singleton(); // 產生一個執行個體
Thread2: instance = new Singleton(); // 又產生一個執行個體

Thread1: return instance; // 回傳一個執行個體
Thread2: return instance; // 又回傳一個執行個體

在多執行緒的環境下,為了避免資源同時競爭而導致如上產生多個執行個體的情況,加上同步(synchronized)機制:
public class Singleton {
    private static Singleton instance = null;
    private Singleton(){}
    synchronized static public Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

不過這種簡單的寫法不適合用於像伺服器這種服務很多執行緒的程式上,同步機制會造成相當的效能低落,為了顧及Singleton、Lazy Initialization與效能問題,因而有了Double-check Locking的模式:
public class Singleton {
    private static Singleton instance = null;
    private Singleton(){}
    public static Singleton getInstance() {
        if (instance == null){
            synchronized(Singleton.class){
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Java中Runtime類別的作法就簡單多了,它捨棄了Lazy Initialization,如果您的執行個體初始化不是很久的話,可以用這種方式:
public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton() {
        // ....
    }

    public static Singleton getInstance() {
        return instance;
    }

    // 其它實作
}

Singleton本身的觀念簡單但應用很廣,因而很多時候必須對實際環境作一些考量與調整,建議您也看看有關於Singleton的這篇 討論。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.