6

Java 5 以降のみ。マルチプロセッサの共有メモリ コンピューターを想定します (おそらく、現在使用しているコンピューターの 1 つです)。

シングルトンの遅延初期化のコードは次のとおりです。

public final class MySingleton {
  private static MySingleton instance = null;
  private MySingleton() { } 
  public static MySingleton getInstance() {
    if (instance == null) {
      synchronized (MySingleton.class) {
        if (instance == null) {
          instance = new MySingleton();
        }
      }
    }
    return instance;
  }
}

オプティマイザーが次のように getInstance() を書き換えないようinstanceに宣言する必要があります (順次プログラムでは正しいでしょう)。volatile

public static MySingleton getInstance() {
  if (instance == null) {
    synchronized (MySingleton.class) {
      // instance must be null or we wouldn't be here  (WRONG!)
      instance = new MySingleton();
    }
  }
}

オプティマイザがコードを書き直さないと仮定すると、instance宣言されていない場合でも、ブロックが終了volatileしたときにメモリにフラッシュされ、ブロックが入力されたときにメモリから読み取られることが保証されますか?synchronizedsynchronized

編集: getInstance() を静的にするのを忘れていました。それが答えの妥当性を変えるとは思いません。あなたは皆、私が何を意味するかを知っていました。

4

5 に答える 5

15

はい、instance宣言する必要がありますvolatile。それでも、ダブルチェックロックを使用しないことをお勧めします。これ(正確には、Javaメモリモデル)には、部分的に実装されたオブジェクトの公開を可能にする重大な欠陥がありました。これはJava5で修正されましたが、DCLは廃止されたイディオムであり、もう使用する必要はありません。代わりに、遅延初期化ホルダーイディオムを使用してください。

Java Concurrency in Practiceのセクション16.2から:

DCLの実際の問題は、同期せずに共有オブジェクト参照を読み取るときに発生する可能性のある最悪の事態は、誤って古い値(この場合)を確認することであるという仮定nullです。その場合、DCLイディオムは、ロックを保持したまま再試行することでこのリスクを補います。ただし、最悪の場合は実際にはかなり悪化します。参照の現在の値を確認できますが、オブジェクトの状態の値が古くなっている可能性があります。つまり、オブジェクトが無効または不正な状態にあると見なされる可能性があります。

その後のJMM(Java 5.0以降)の変更により、DCLが実行された場合に機能するよう になりました。読み取りは通常、不揮発性読み取りよりもわずかに高価であるため、パフォーマンスへの影響はわずかです。ただし、これは実用性が大幅に向上したイディオムです。これを動機付けた力(競合のない同期の遅延、JVMの起動の遅延)は機能しなくなり、最適化としての効果が低下します。遅延初期化ホルダーイディオムは同じ利点を提供し、理解しやすくなります。resourcevolatilevolatile

于 2010-08-30T15:07:08.583 に答える
1

Java シングルトンの遅延初期化には、より安全で読みやすいイディオムがあります。

class Singleton {

  private static class Holder {
    static final Singleton instance = create();
  }

  static Singleton getInstance() { 
    return Holder.instance; 
  }

  private Singleton create() {
    ⋮
  }

  private Singleton() { }

}

より冗長な Double-Checked Locking パターンを使用する場合は、volatile他の人が既に述べたように、フィールドを宣言する必要があります。

于 2010-08-30T15:40:04.780 に答える
1

はい、Java でダブルチェック ロックを使用してインスタンスを揮発性にする必要があります。そうしないと、MySingleton の初期化によって部分的に構築されたオブジェクトがシステムの残りの部分に公開される可能性があるためです。「同期」ステートメントに到達するとスレッドが同期することも事実ですが、この場合は遅すぎます。

ウィキペディアと他のいくつかのスタック オーバーフローに関する質問には、「ダブルチェック ロック」についての良い議論があるので、それについて読むことをお勧めします。また、プロファイリングでこの特定のコードのパフォーマンスが本当に必要であることが示されない限り、使用しないことをお勧めします。

于 2010-08-30T15:04:07.807 に答える
0

http://www.javaworld.com/jw-02-2001/jw-0209-double.htmlを参照してください。

于 2010-08-30T15:01:48.273 に答える
0

いいえ、揮発性である必要はありません。Javaで「ダブルチェックロックが壊れています」宣言を解決する方法を参照してください。

以前の試みはすべて失敗しました。なぜなら、Java をごまかして volatile/sync を避けることができれば、Java はあなたをごまかし、オブジェクトの誤ったビューを与える可能性があるからです。ただし、新しいfinalセマンティクスが問題を解決します。オブジェクト参照を (通常の読み取りで) 取得した場合は、そのfinalフィールドを安全に読み取ることができます。

于 2010-08-30T20:29:56.943 に答える