2

「ダブルチェックロック」のケースに遭遇している可能性があると思われるアプリのコードを見ています。私たちが行っているのと同様のサンプルコードをいくつか作成しました。

誰かがこれがダブルチェックロックをどのように経験しているのかを見ることができますか?それともこれは安全ですか?

class Foo {
    private Helper helper = null;
    public Helper getHelper() {
        Helper result;
        synchronized(this) {
            result = helper;
        }

        if (helper == null) {
            synchronized(this) {
                if (helper == null) {
                    helper = new Helper();
                }
            }
        }
        return helper;
    }
}

wikiから借用したベースコード。

4

3 に答える 3

6

これは不必要に複雑で、DCLを実行する最も簡単な「安全な」方法は次のようになります。

class Foo {
  private volatile Helper helper = null;
  private final Object mutex = new Object(); 
  public Helper getHelper() {
    if (helper == null) {
        synchronized(mutex) {
            if (helper == null) {
                helper = new Helper();
            }
        }
    }
    return helper;
  }
}

ここでの重要なポイントは次のとおりです。

  • 「ハッピー」の場合、ヘルパーがすでに割り当てられていることを期待しているので、そうである場合は、同期ブロックに入る必要なしにヘルパーを返すことができます。
  • ヘルパーは、いつでも任意のスレッドからヘルパーの読み取り/書き込みが可能であり、読み取り/書き込みの順序が変更されないことが重要であることをコンパイラーに通知するために、揮発性としてマークされています。
  • 同期されたブロックは、プライベートfinal変数を使用して同期し、インスタンスで同期しているコードの別の領域の場合にパフォーマンスが低下する可能性を回避しthisます。
于 2011-12-06T23:01:51.363 に答える
3

ダブルチェックロックの要点は、高速パス(オブジェクトをインスタンス化する必要がない場合)が同期されていないことです。したがって、あなたが持っているのは、ロックを再確認することではありません。

壊れたダブルチェックロックを取得するには、最初に同期されたブロックを取り除く必要があります。helper次に、それを修正するために揮発性にする必要があります。

于 2011-12-06T23:03:44.677 に答える
0

この部分は、標準のダブルチェックロックです。

if (helper == null) {
    synchronized(this) {
        if (helper == null) {
            helper = new Helper();
        }
    }
}

最初の部分は、ダブルチェックされたロック部分には何もしない役に立たない割り当てです。ヘルパーがnullの場合はとにかく実行され、そうでない場合はとにかく実行されません。それは完全に効果がありません。

于 2011-12-06T23:05:01.513 に答える