Singletonパターンの罠

インスタンスが一個しか存在しないクラスを書くためのパターンがSingleton Patternです.登場するのはSingletonクラスひとつのみで以下のようなクラス図になります.

L

結城本には以下のような感じでサンプルコードが掲載されていました.

public class Singleton {
    private Singleton() {}
    private static Singleton singleton = new Singleton();
    public static Singleton getInstance() {
        return singleton;
    }
}

Double-checked locking 問題

参考ページ

概要

Fooクラスのインスタンスがただ一つだけするようにSingletonパターンを用いて以下のようなコードを書いたとします.

class Foo {
    private Helper helper;
    public Helper getHelper() {
        if (helper == null) {      // [1]
            helper = new Helper(); // [2]
        }
        return helper;
    }
 
    // other functions and members...
}

もし2つのスレッドが同時にgetHelper()を呼んだ場合,同時にインスタンスを生成しようとして,片方が完全に初期化されないままのインスタンスの参照を持ってしまうようなことが起こり得ます.これを防ぐためには,コストは大きいですが同期を取ってあげればよく,以下のようなコードに変更すれば良いです(from wikipedia).

// Correct but possibly expensive multithreaded version
class Foo {
    private Helper helper;
    public synchronized Helper getHelper() {
        if (helper == null) {
            helper = new Helper();
        }
        return helper;
    }
 
    // other functions and members...
}

よくよく考えてみるとsynchronizeが必要なのはgetHelper()内の最初のif文のみです.そこでDouble-checked lockingというイディオムが考えられました(from wikipedia).

public static Singleton getInstance()
{
  if (instance == null)
  {
    synchronized(Singleton.class) {  //1
      if (instance == null)          //2
        instance = new Singleton();  //3
    }
  }
  return instance;
}

Initialization-on-demand holder idiom

Double-checked lockingによりsynchronizedにより初回に正しくインタスタンス生成されたあとは同期を取らずにすみます.理論的には申し分ないのですが,これはJavaプラットフォームのメモリー・モデルが原因で,期待通りの動作が必ずしも保証されません.Javaではシングルトン実装のアンチパターンとされています.そこでInitialization-on-demand holder idiomというものが推奨されています(from wikipedi).

public class Something {
    private Something() {}

    private static class LazyHolder {
        private static final Something INSTANCE = new Something();
    }

    public static Something getInstance() {
        return LazyHolder.INSTANCE;
    }
}

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください