設計模式之——Singleton pattern

weixin_33751566發表於2016-07-20

單例模式網址

Singleton pattern - wikipedia

單例模式定義

在一個系統中,一個類只有一個例項化的物件。

單例模式應用

  • The abstract factory, builder, and prototype patterns can use Singletons in their implementation.
  • Facade objects are often singletons because only one Facade object is required.
  • State objects are often singletons.
  • Singletons are often preferred to global variables because:
    • They do not pollute the global namespace (or, in languages with namespaces, their containing namespace) with unnecessary variables.<sup id="cite_ref-4" class="reference" style="line-height: 1; unicode-bidi: -webkit-isolate; white-space: nowrap; font-size: 12px; font-weight: normal; font-style: normal;">
    • They permit lazy allocation and initialization, whereas global variables in many languages will always consume resources.

實現方案

The singleton pattern is implemented by creating a class with a method that creates a new instance of the class if one does not exist. If an instance already exists, it simply returns a reference to that object.

可以實現延遲載入,即使用之前不佔用記憶體或資源。(Note the distinction between a simple static instance of a class and a singleton: although a singleton can be implemented as a static instance, it can also be lazily constructed, requiring no memory or resources until needed.)

The singleton pattern must be carefully constructed in multi-threaded applications. If two threads are to execute the creation method at the same time when a singleton does not yet exist, they both must check for an instance of the singleton and then only one should create the new one. If the programming language has concurrent processing capabilities the method should be constructed to execute as a mutually exclusive operation. The classic solution to this problem is to use mutual exclusion on the class that indicates that the object is being instantiated.

Lazy initialization

使用“雙重檢查加鎖”——double-checked locking。其中變數 instance 是 volatile 的,1.4及更早的Java中,volatile關鍵字的實現會導致雙重檢查加鎖的失效。具體原因,參考 The "Double-Checked Locking is Broken" Declaration

public final class SingletonDemo {
    private static volatile SingletonDemo instance;
    private SingletonDemo() { }

    public static SingletonDemo getInstance() {
        if (instance == null ) {
            synchronized (SingletonDemo.class) {
                if (instance == null) {
                    instance = new SingletonDemo();
                }
            }
        }

        return instance;
    }
}

在併發性並不是很高,效能並不特別需求的情況下,可以使用下面簡潔的方法:

public final class SingletonDemo {
    private static SingletonDemo instance = null;
    private SingletonDemo() { }

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

        return instance;
    }
}

Eager initialization

如果系統總是需要一個例項,或者建立例項的開銷並不大,可以使用Eager initialization,它總是返回一個例項。

public final class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

這種方法的好處:

  1. 在類載入之後、被任何執行緒使用之前,static 的變數 INSTANCE 就已經定義好。
  2. 方法getInstance()不需要同步,所有的執行緒都會看到同樣的例項。
  3. 關鍵字final使得 INSTANCE 不能修改,保證有且只有一個例項。

Static block initialization

static程式碼塊中建立新例項,同Eager initialization

public final class Singleton {
    private static final Singleton instance;

    static {
        try {
            instance = new Singleton();
        } catch (Exception e) {
            throw new RuntimeException("Darn, an error occurred!", e);
        }
    }

    public static Singleton getInstance() {
        return instance;
    }

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

Initialization-on-demand holder idiom

  1. 儘可能延遲載入
  2. 適用於所有的 Java 版本
  3. 執行緒安全
public final class Singleton {
        // Private constructor. Prevents instantiation from other classes.
        private Singleton() { }

        private static class SingletonHolder {
                private static final Singleton INSTANCE = new Singleton();
        }

        public static Singleton getInstance() {
                return SingletonHolder.INSTANCE;
        }
}

內部類SingletonHolder在方法getInstance()被呼叫時,才會被 class loader 載入。因此這個方法是執行緒安全的,不需要額外的同步手段。內部類SingletonHolder也可以被定義成final的。

The enum way

In the second edition of his book Effective Java, Joshua Bloch claims that "a single-element enum type is the best way to implement a singleton" for any language that supports enums, like Java.

public enum Singleton {
    INSTANCE;
    public void execute (String arg) {
        // Perform operation here 
    }
}

Java語言保證,列舉型別會在需要使用時,通過 class loader 載入。同時 Java 的列舉型別是全域性可見的,但是有些呆板 0_o。

相關文章