デザインパターンの章

6.2. Singleton

たった一つのインスタンスしか作らせないようにするパターンです。

普通はインスタンスを沢山作るものですが、場合によってはインスタンスを一つしか作らない、 作らせたくないということもあるでしょう。そのようなときに威力を発揮します。

 Java API での使用例 java.lang.Runtime

もっとも、一つしか作りたくないのであれば newの呼び出しを1回だけにすればよいと思うかもしれません。 プログラマが注意深く実装すればそれでも可能です。

しかしプログラマ任せにすると、間違ってnewを複数回呼び出してしまうかもしれません。
絶対ではありません。

このパターンを適用すると、 指定したクラスのインスタンスが絶対に1つしか存在しないことを保証することができます。

■クラス図

■サンプルソース

実装方法は簡単です。

public class Hoge {
    /* 唯一のインスタンス。 */
    private static final Hoge instance = new Hoge();

    /**
     * コンストラクタ。
     */
    private Hoge() {
    }

    /**
     * このクラスの唯一のインスタンスを返す。
     */
    public static Hoge getInstance() {
        return instance;
    }
}

いわゆる「自己参照」の形を取ります。

■実装のミソ

1.
private な static 変数を定義して初期化します。
インスタンスは、このクラスのロード時に一度だけ生成処理が行われることにより作られます。

2.
コンストラクタは外部に公開せず private とします。
そうすることで外部からうっかりインスタンス生成されることを防ぎます。
privateにしないと外部から new Hoge()とされてしまいインスタンスが自由に作れてしまいます。

3.
getInstance() を作成し外部に公開します。
作られた唯一のインスタンスを返却することがこのメソッドの役目です。
public メソッドにしてどこからでも呼び出せるようにします。

どう頑張ってもこのクラスのインスタンスは一つしか生成されなくなります。
なお、生成のタイミングは、初めて getInstance() をが呼ばれた時です。

■利用例

void Test() {
    /* new できません。コンパイルエラーとなります。 */
    // Hoge hoge = new Hoge();
    // この時点で Hoge インスタンス生成&返却
    Hoge hoge1 = Hoge.getInstance();
    // 同じインスタンス返却
    Hoge hoge2 = Hoge.getInstance();
    if (hoge1 == hoge2) {
        System.out.println("同じインスタンスです。");
    }
}

Hoge.getInstance() を呼び出すことで Hoge インスタンスが返ってきます。
Hoge.getInstance() を何度呼び出しても同じインスタンスが返ってきます。
常に唯一のインスタンスが返されます。

getInstance() 以外にHogeインスタンスを得る方法はないので、 唯一のインスタンスを使い回すことができるのです。

■やってはいけない実装方法

某専門書や某ウェブサイトに、以下のような Singleton 実装方法が紹介されていました。

public class Hoge {
    /* 唯一のインスタンス。 */
    private static final instance; // 初期化しない (= null と同義)

    /**
     * コンストラクタ。
     */
    private Hoge() {
    }

    /**
     * このクラスの唯一のインスタンスを返す。
     */
    public static Hoge getIntance() {
        if (instance == null) {    // null だったら new する
            instance = new Hoge();
        }
        return instance;
    }
}

これは、C言語で書かれていたものをJavaへ単純にベタ移植したものですが、 状況によっては期待通りに動きません。

マルチスレッド動作において問題になります。
ほぼ同タイミングで別々のスレッドから getIntance() を呼び出した際に 「複数」のインスタンスを生成してしまうことがあります。 唯一のインスタンスという前提がもろくも崩れ去るという欠陥を持ってしまっています。

複数のインスタンスが作られないように getIntance() を同期化(synchronize)したり、 ダブルチェックという技法を使うことで問題を回避することはできますが、 同期化することでパフォーマンスを少なからず犠牲にしますし、 コードもその分煩雑になってしまいます。
よって、お勧めしかねます。

■シングルトンなインスタンスはずっと生存し続ける

通常のインスタンスは、どこからも参照されなくなるとガベージコレクションに拾われて、 やがてメモリ解放されます。 しかし、シングルトンなインスタンスは自分で参照し続けているので ガベージコレクションの対象とはなりません。

よって、シングルトンなインスタンスが他のインスタンスを掴んでいると、 掴んでいるインスタンスもガベージコレクション対象外となります。

あまりにも掴んでいるインスタンスが多いとそれだけ使用メモリがかさみます。 また、インスタンスを離す処理(remove や null 代入)を忘れると いつまでも掴んでいることになります。

すると、メモリが解放されずにメモリリークの状態になり動作が遅くなったり OutOfMemory 例外でアプリケーションが動かなくなってしまうことがあります。

インスタンスの管理はきっちり行う必要があります。
解放忘れをするとアプリケーションの不正終了など悲惨な事態を招きます。

< 前のページへ

Pagetop