6

http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.htmlの下部には、次のように書かれています。

ダブルチェックされた不変オブジェクトのロック

Helper のすべてのフィールドが final であるなど、Helper が不変オブジェクトである場合、再確認されたロックは揮発性フィールドを使用しなくても機能します。不変オブジェクト (String や Integer など) への参照は、int や float とほぼ同じように動作する必要があるという考え方です。不変オブジェクトへの参照の読み取りと書き込みはアトミックです。

変更可能なもののサンプルと説明は次のとおりです。

// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo { 
  private Helper helper = null;
  public Helper getHelper() {
    if (helper == null) 
      synchronized(this) {
        if (helper == null) 
          helper = new Helper();
      }    
    return helper;
    }
  // other functions and members...
  }

うまくいかない一番の理由

それが機能しない最も明白な理由は、ヘルパー オブジェクトを初期化する書き込みとヘルパー フィールドへの書き込みが実行されたり、順不同で認識されたりする可能性があるためです。したがって、getHelper() を呼び出すスレッドは、ヘルパー オブジェクトへの null 以外の参照を参照できますが、コンストラクターで設定された値ではなく、ヘルパー オブジェクトのフィールドのデフォルト値を参照できます。

コンパイラーがコンストラクターへの呼び出しをインライン化する場合、コンストラクターが例外をスローしたり、同期を実行したりできないことをコンパイラーが証明できれば、オブジェクトを初期化する書き込みとヘルパー フィールドへの書き込みを自由に並べ替えることができます。

コンパイラがこれらの書き込みを並べ替えない場合でも、マルチプロセッサでは、別のプロセッサで実行されているスレッドによって認識されるように、プロセッサまたはメモリ システムがそれらの書き込みを並べ替える場合があります。

私の質問は、なぜ不変クラスに問題がないのですか? クラスが変更可能かどうかとの並べ替えの関係はわかりません。

ありがとう

4

2 に答える 2

1

通常のオブジェクトのコードが「壊れている」理由は、null ではない可能性がありますが、引用で説明されているように、まだ完全に初期化helperされていないオブジェクトを指している可能性があるためです。

ただし、ヘルパークラスが不変である場合、つまりそのすべてのフィールドが最終的なものである場合、Java メモリモデルは、オブジェクトがデータ競合によって利用可能になった場合でも、それらが安全に公開されることを保証します (これはあなたの例の場合です):

finalフィールドを使用すると、プログラマーは同期なしでスレッドセーフな不変オブジェクトを実装することもできます。スレッド間で不変オブジェクトへの参照を渡すためにデータ競合が使用されている場合でも、スレッド セーフな不変オブジェクトはすべてのスレッドで不変と見なされます。これにより、不正または悪意のあるコードによる不変クラスの悪用に対する安全性が保証されます。final不変性を保証するには、フィールドを正しく使用する必要があります。

于 2014-10-28T15:36:31.540 に答える