「ダブル・チェック・ロッキング」の版間の差分

imported>Administrator
編集の要約なし
 
(2人の利用者による、間の2版が非表示)
1行目: 1行目:
'''ダブル・チェック・ロッキング'''([[英語]]:double check locking)とは、[[マルチスレッド]]環境下における[[変数]]に対して、初回は[[ロック]]せず状態チェックを行い、そこで必要であれば再度[[ロック]]を掛けた後に状態チェックを行うという[[ソフトウェア]]の[[最適化]]技法、[[デザインパターン]]のひとつである。
'''ダブル・チェック・ロッキング'''([[英語]]:double check locking)とは、[[マルチスレッド]]環境下における[[変数]]に対して、初回は[[ロック]]せず大雑把に状態チェックを行い、そこで必要であれば再度[[ロック]]を掛けた後に厳密に状態チェックを行うという[[ソフトウェア]]の[[最適化]]技法、[[デザインパターン]]のひとつである。


==概要==
==概要==
ダブル・チェック・ロッキングは、主に[[マルチスレッド]]環境下での[[シングルトンパターン]]を実装する際に[[オーバーヘッド]]の低減を目的として使われることが多い。
ダブル・チェック・ロッキングは、主に[[マルチスレッド]]環境下での[[シングルトンパターン]]を実装する際に[[オーバーヘッド]]の低減を目的として使われることが多い。


マルチスレッド下における[[変数]]の[[ロック]]は非常に[[オーバーヘッド]]が大きくスレッド数に比例して重くなる。ダブル・チェック・ロッキングはそのような状況下でロックの発生回数を可能な限り減らすことで[[プログラム]]の高速化を実現しようというものである。なお、シビアな速度を要求されないのであれば[[ソースコード]]としては何も考えず常にロックした方が簡潔明瞭ではある。
マルチスレッド下における[[変数]]の[[ロック]]は非常に[[オーバーヘッド]]が大きくスレッド数に比例して重くなる。ダブル・チェック・ロッキングはそのような状況下でロックの発生回数を可能な限り減らすことで[[プログラム]]の高速化を実現しようというものである。
 
なお、シビアな速度を要求されないのであれば[[ソースコード]]の[[可読性]]としては何も考えず常にロックした方が簡潔明瞭ではある。


== 主なプログラミング言語での実装例 ==
== 主なプログラミング言語での実装例 ==
24行目: 26行目:
     public static MySingleton GetInstance()
     public static MySingleton GetInstance()
     {
     {
         // 1回目のチェック
         // 1回目の大雑把なチェック
         // ロックしていないので高速に処理される
         // ロックしていない「nullではない場合」の処理が高速化されます。
         if (null == _instance)
         if (null == _instance)
         {
         {
             // ロック
             // ロック
             // ※このブロック内はクソ重い
             // ここで処理は急激に重くなります。
             lock (_sync)
             lock (_sync)
             {
             {
                 // 2回目のチェック
                 // 2回目の厳密なチェック
                 if (null == _instance)
                 if (null == _instance)
                 {
                 {
77行目: 79行目:


=== Java ===
=== Java ===
[[Java]]では[[仕様]]において[[アウトオブオーダー]]を用いるメモリモデルが可能となっていたため、この[[イディオム]]を使うことには問題があり、一部の[[実装]]では実際に正しく働かない可能性があることが知られている。詳細は http://www.ibm.com/developerworks/jp/java/library/j-dcl/ を参照。
[[Java]]では[[仕様]]において[[アウトオブオーダー]]を用いるメモリモデルが可能となっていたため、この[[イディオム]]を使うことには問題があり、一部の[[実装]]では実際に正しく働かない可能性があることが知られている。詳細は http://www.ibm.com/developerworks/jp/java/library/j-dcl/ を参照。大雑把にいえばJavaでは「new(メモリ確保)」と「コンストラクタ実行」のタイミングが異なる。


[[Java]]では絶対にダブルチェックロッキングを使用してはならない。
よって、[[Java]]では絶対にダブルチェックロッキングを使用してはならない。


たとえば以下のような[[Java]]の[[ソースコード]]があったとする。
たとえば以下のような[[Java]]の[[ソースコード]]があったとする。
89行目: 91行目:
# 1行目で[[メモリ]]が確保される。
# 1行目で[[メモリ]]が確保される。
#: いわゆるmallocが実行されhage[[変数]]自体は[[null]]ではなくなる。
#: いわゆるmallocが実行されhage[[変数]]自体は[[null]]ではなくなる。
#: ただし[[コンストラクタ]]はまだ実行されない。
#: ただし[[コンストラクタ]]はまだ実行されていない。
# 2行目の初めて使うときにコンストラクタが実行される。
# 2行目のインスタンスを初めて使うときにコンストラクタが実行される。
#: [[コンストラクタ]]が実行されるタイミングは、オリジナル(newを実行した[[スレッド]]の持つ)インスタンスに対して外部から[[メンバー関数]]や[[メンバー変数]]に初回アクセスがあったときとなる。これを[[遅延初期化]](lazy initialization)という。これによりhage[[変数]]自体は[[null]]ではないので後続スレッドはコンストラクタを実行していない不完全な[[インスタンス]]の[[参照]]を取得できてしまう。
#: [[コンストラクタ]]が実行されるタイミングは、オリジナル(newを実行した[[スレッド]]の持つ)インスタンスに対して外部から[[メンバー関数]]や[[メンバー変数]]に初回アクセスがあったときとなる。これを[[遅延初期化]](lazy initialization)という。これによりhage[[変数]]自体は[[null]]ではないので後続スレッドはコンストラクタを実行していない不完全な[[インスタンス]]の[[参照]]を取得できてしまう。