Shammer's Philosophy

My private adversaria

ダブルチェックでも不十分・・・

JavaでSingletonなどを実装するときに、インスタンスフィールドが
複数スレッドからアクセスされないようにするためのテクニックとして、
ダブルチェックという手法がよく使われる。こんな感じ。

 public final class SingletonService {
  private static final SingletonService singleton;
  private SomeObject object = new SomeObject();
  private SingletonService() {
  }
  public static SingletonService getInstance(){
   if( singleton == null ){※1
    synchronized( SingletonService.class ){
     if( singleton == null ){
      singleton = new SingletonService();※2
     }
    }
   }
   return singleton;
  }
  public SomeObject getSomeObject(){
   return object;
  }
 }

複数スレッドが同時にこのSingletonService.getInstance() を呼び出すと、
コンストラクタ内のsynchronizedブロックがないと複数のobjectが作られてしまい、
スレッドごとに同一クラス同一インスタンスが異なる参照先を保持してデッドロックのような
状態になってしまう。これを防ぐためのsynchronizedと、二度objectのnullチェックを行うコードを
ダブルチェックと呼ぶが、どうもこれでもNGらしい。


具体的には、最初のスレッドが※2を終えてsynchronizedブロックを抜ける前に、
次のスレッドが※1で singleton != null と判断し、その後singleton.getSomeObject()を実行すると、
非常にまれなタイミングでnullが返る可能性がある。syncronizedを抜ければ、
SingletonServiceの初期化は完了しているからインスタンスフィールドもnullではなくなっているが。
こういうタイミングはどれだけ存在するのだろう。


自分が過去に書いたダブルチェックのコード、修正したいなぁ。
まさかこんな落とし穴があるとは・・・う〜ん。