以下のコードを検討してください
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
if (instance == null) //2
instance = new Singleton(); //3
}
}
return instance;
}
ダブルチェック ロックの背後にある理論は、//2 での 2 番目のチェックにより、リストで発生したように 2 つの異なるシングルトン オブジェクトを作成できなくなるというものです。
次の一連のイベントを検討してください。
スレッド 1 が getInstance() メソッドに入ります。
インスタンスが null であるため、スレッド 1 は //1 で同期ブロックに入ります。
スレッド 1 はスレッド 2 によって横取りされます。
スレッド 2 は getInstance() メソッドに入ります。
インスタンスがまだ null であるため、スレッド 2 は //1 でロックを取得しようとします。ただし、スレッド 1 がロックを保持しているため、スレッド 2 は //1 でブロックされます。
スレッド 2 はスレッド 1 によって横取りされます。
スレッド 1 が実行され、インスタンスが //2 でまだ null であるため、Singleton オブジェクトを作成し、その参照をインスタンスに割り当てます。
スレッド 1 は同期ブロックを終了し、getInstance() メソッドからインスタンスを返します。
スレッド 1 はスレッド 2 によって横取りされます。
スレッド 2 は //1 でロックを取得し、インスタンスが null かどうかを確認します。
instance は null ではないため、2 番目の Singleton オブジェクトは作成されず、スレッド 1 によって作成されたオブジェクトが返されます。
ダブルチェック ロックの背後にある理論は完璧です。残念ながら、現実はまったく異なります。ダブルチェック ロックの問題は、シングル プロセッサまたはマルチ プロセッサ マシンで動作する保証がないことです。ダブルチェック ロックの失敗の問題は、JVM の実装バグによるものではなく、現在の Java プラットフォームのメモリ モデルによるものです。メモリ モデルでは、いわゆる「順不同の書き込み」が可能であり、これがこのイディオムが失敗する主な理由です。