11

私は「ダブルチェックロック」に関するこの記事を読んでいて、記事のメイントピックから離れて、なぜ記事のある時点で著者が次のイディオムを使用するのか疑問に思っていました:

リスト 7. 順不同の書き込み問題の解決を試みる

public static Singleton getInstance()  
{
    if (instance == null)
    {
        synchronized(Singleton.class) {      //1
            Singleton inst = instance;         //2
            if (inst == null)
            {
                synchronized(Singleton.class) {  //3
                    inst = new Singleton();        //4
                }
                instance = inst;                 //5
            }
        }
    }
    return instance;
}

そして私の質問は、同じロックでコードを 2 回同期する理由はありますか? これは何か目的がありますか?

よろしくお願いします。

4

10 に答える 10

15

2 回ロックするのは、順不同の書き込みを防止するためですメモリー・モデルは、部分的にロックの観点から、再順序付けが発生する可能性がある場所を指定します。ロックにより、"instance = inst;" の後に書き込み (シングルトン コンストラクター内の書き込みを含む) が発生しないことが保証されます。ライン。

ただし、このテーマをさらに深く掘り下げるには、 Bill Pugh の記事をお勧めします。そして、決してそれを試みないでください:)

于 2008-10-01T11:41:26.570 に答える
13

この記事では、5.0 より前の Java メモリ モデル (JMM) について言及しています。そのモデルでは、同期ブロックを残すと、メイン メモリへの書き込みが強制されます。そのため、Singleton オブジェクトが参照される前に確実に押し出されるようにする試みのようです。ただし、インスタンスへの書き込みはブロック (roach motel) に移動できるため、うまくいきません。

ただし、5.0 より前のモデルは正しく実装されていませんでした。1.4 は 5.0 モデルに従う必要があります。クラスは遅延して初期化されるため、次のように書くこともできます

public static final Singleton instance = new Singleton();

または、シングルトンは悪であるため使用しないでください。

于 2008-10-01T12:38:00.427 に答える
6

Jon Skeet の言うとおりです。Bill Pugh の記事を読んでください。Hans が使用する慣用句は正確な形式であり、機能しないため、使用すべきではありません。

これは安全ではありません:

private static Singleton instance;

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

これも安全ではありません:

public static Singleton getInstance()  
{
    if (instance == null)
    {
        synchronized(Singleton.class) {      //1
            Singleton inst = instance;         //2
            if (inst == null)
            {
                synchronized(Singleton.class) {  //3
                    inst = new Singleton();        //4
                }
                instance = inst;                 //5
            }
        }
    }
    return instance;
}

どちらも絶対にしないでください。

代わりに、メソッド全体を同期します。

    public static synchronized Singleton getInstance() {
      if (instance == null) {
        instance = new Singleton();
      }
      return instance;
    }

このオブジェクトを 1 秒間に何億回も取得しない限り、実際のパフォーマンスへの影響は無視できます。

于 2008-10-01T12:07:36.727 に答える
3

私はここでこれをカバーします:

http://tech.puredanger.com/2007/06/15/double-checked-locking/

于 2008-10-01T15:44:39.933 に答える
1

ジョン・スキートの勧告に従う:

ただし、このテーマをさらに深く掘り下げるには、Bill Pugh の記事をお勧めします。そして、決してそれを試みないでください:)

2 番目の同期ブロックのキーは次のとおりです。

このコードは、Helper オブジェクトの構築を内部の同期ブロック内に配置します。ここでの直感的な考えは、同期が解放された時点でメモリ バリアが必要であり、ヘルパー オブジェクトの初期化とフィールド ヘルパーへの割り当ての順序変更を防止する必要があるということです。

基本的に、内部同期ブロックを使用して、同期ブロック内でインスタンスを作成する JMM を「ごまかす」ことで、同期ブロックが終了する前に JMM にその割り当てを強制的に実行させようとしています。しかし、ここでの問題は、JMM が先頭に立ち、同期ブロック内の同期ブロックの前にある割り当てを移動して、問題を最初に戻すことです。

これは私がそれらの記事から理解したものであり、非常に興味深いものであり、もう一度返信に感謝します.

于 2008-10-01T13:48:37.233 に答える
0

このイディオムに関しては、非常に賢明で明確な記事があります。

http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html?page=1

一方、dhighwayman.myopenidが意味するのは、ライターが同じクラスを参照する1つの同期ブロック(synchronized(Singleton.class))を、同じクラスを参照する別の同期ブロック内に配置した理由だと思います。新しいインスタンス(Singleton inst = instance;)がそのブロック内に作成され、スレッドセーフであることを保証するために、別の同期されたインスタンスを作成する必要がある場合に発生する可能性があります。

そうでなければ、私は何の意味も見ることができません。

于 2008-10-01T13:22:48.307 に答える
0

Java 5以降、フィールドを揮発性として宣言することにより、ダブルチェックロックを機能させることができます。

詳細な説明については、http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.htmlを参照してください。

于 2008-10-01T12:44:03.720 に答える
0

Java 5 以降の場合、実際には、アクセサー全体を同期するよりも優れた二重チェックのバリアントがあります。これは、Double-Checked Locking Declarationにも記載されています。

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

ここでの重要な違いは、変数宣言でのvolatileの使用です。それ以外の場合は機能しません。とにかく、Java 1.4 以下では機能しません。

于 2010-01-25T11:32:13.590 に答える
0

Java メモリ モデルに関する Google Tech Talk を参照してください。JMM の細かい点についての非常に優れた紹介です。ここでは欠落しているため、Jeremy Manson のブログ「Java Concurrency」も指摘したいと思います。Double Checked Lockingに関する投稿(Java の世界では誰でも、これに関する記事を持っているようです :)。

于 2008-12-27T16:52:43.673 に答える
0

わかりましたが、記事には次のように書かれていました

リスト 7 のコードは、メモリ モデルの現在の定義が原因で機能しません。Java 言語仕様 (JLS) では、同期ブロック内のコードを同期ブロックの外に移動しないことが要求されています。ただし、同期ブロックにないコードを同期ブロックに移動できないとは言いません。

また、JVM が ASM の「疑似コード」に次の変換を行うようです。

public static Singleton getInstance()
{
  if (instance == null)
  {
    synchronized(Singleton.class) {      //1
      Singleton inst = instance;         //2
      if (inst == null)
      {
        synchronized(Singleton.class) {  //3
          //inst = new Singleton();      //4
          instance = new Singleton();               
        }
        //instance = inst;               //5
      }
    }
  }
  return instance;
}

これまでのところ、「instance=inst」以降の書き込み禁止のポイントは達成されていませんか?

リンクをありがとう、私は今記事を読みます。

于 2008-10-01T11:51:20.120 に答える