15
class MyClass
{
      private static volatile Resource resource;

      public static Resource getInstance()
      {
            if(resource == null)
                  resource = new Resource();
            return resource;
      }
 }

ここで私の疑問は、揮発性を使用する場合、実際にはJavaの同時実行性によるものであり、安全な公開が行われます(つまり、参照が別のスレッドに表示されるとすぐにデータも利用可能になります)。じゃあここ使っていい?しかし、それが正しければ、thread1 が "resource" をチェックし、それが null であるため、オブジェクトの作成を開始するとします。スレッド 1 がオブジェクトを作成している間に、別のスレッド、つまりスレッド 2 が来て「リソース」の値のチェックを開始し、スレッド 2 がそれを null として検出します (「リソース」オブジェクトの作成にはかなりの時間がかかり、スレッド 1 がまだ作成を完了していないため、安全なパブリケーションが発生していないため、スレッド 2 では使用できません)では、オブジェクトの作成も開始されますか? はいの場合、クラスの不変条件が壊れます。私は正しいですか?ここでの volatile の特別な使用を理解するのを手伝ってください。

4

9 に答える 9

19

その通りです。複数のスレッドが Resource オブジェクトを作成しようとする可能性があります。Volatile は、1 つのスレッドが参照を更新すると、他のすべてのスレッドが、キャッシュされた参照ではなく、新しい参照を参照することを保証します。これは遅くなりますが、より安全です。

遅延ロードされる単一のリソースのみが必要な場合は、次のようにする必要があります。

class MyClass
{
      private static volatile Resource resource;
      private static final Object LOCK = new Object();

      public static Resource getInstance()
      {
            if(resource == null) { 
                synchronized(LOCK) { // Add a synch block
                    if(resource == null) { // verify some other synch block didn't
                                           // write a resource yet...
                        resource = new Resource();
                    }
                }
            }
            return resource;
      }
 }
于 2013-02-25T22:42:58.663 に答える
7

より良いソリューションについて質問していないことはわかっていますが、怠惰なシングルトン ソリューションを探している場合、これは間違いなく価値があります。

プライベート静的クラスを使用してシングルトンをロードします。クラスは呼び出しまでロードされないため、参照はクラスがロードされるまでロードされません。実装によるクラスのロードはスレッドセーフであり、オーバーヘッドもほとんど発生しません (揮発性ロードを繰り返し実行している場合 [それでも安価な場合があります]、この解決策は最初の構築後に常に通常のロードを行います)。

class MyClass {
    public static Resource getInstance() {
        return ResourceLoader.RESOURCE;
    }

    private static final class ResourceLoader {
        private static final Resource RESOURCE = new Resource();
    }
}
于 2013-02-25T22:59:01.357 に答える
1

定義のsyncronized前にキーワードを使用する必要があると思います。getInstance

パフォーマンスを向上させるために、ダブルチェック ロック パターンを使用できます。

于 2013-02-25T22:44:53.643 に答える
0

揮発性と同期を一緒に追加することをお勧めします。

注: まだ二重チェックを行う必要があります。

public class MySingleton {
    private static volatile MySingleton instance;
    private MySingleton() {}

    synchronized private static void newInstance() {
        if(instance == null) {
            instance = new MySingleton();
        }
    }

    public static MySingleton get() {
        if(instance == null) {
            newInstance();
        }
        return instance;
    }
}
于 2016-12-20T18:23:36.187 に答える
0

まず第一に、このようにシングルトンを使用すると、基本的に悪い習慣であるグローバル オブジェクトを作成することになります。代わりに列挙型を使用していると思います。

于 2013-02-25T23:43:00.333 に答える
0

volatileキーワードは、その変数への読み取りと書き込みがアトミックであることを保証します。

チュートリアルによると

Reads and writes are atomic for all variables declared volatile

volatile 変数を使用すると、メモリの整合性エラーのリスクが軽減されます。これは、volatile 変数への書き込みによって、同じ変数の後続の読み取りとの先行発生関係が確立されるためです。これは、volatile 変数への変更が常に他のスレッドから見えることを意味します。さらに、これは、スレッドが volatile 変数を読み取るときに、volatile に対する最新の変更だけでなく、変更を引き起こしたコードの副作用も確認することを意味します。

于 2013-02-25T22:44:37.440 に答える
0

Java volatile をフィールドに適用すると、次のことが保証されます。

  1. (Java のすべてのバージョンで) volatile 変数への読み取りと書き込みにはグローバルな順序があります。これは、揮発性フィールドにアクセスするすべてのスレッドが、(潜在的に) キャッシュされた値を使用する代わりに、続行する前に現在の値を読み取ることを意味します。(ただし、揮発性の読み取りと書き込みと通常の読み取りと書き込みの相対的な順序についての保証はありません。つまり、一般的に有用なスレッド構造ではありません。)

  2. (Java 5 以降) 揮発性の読み取りと書き込みは、ミューテックスの取得と解放と同様に、事前発生の関係を確立します。

詳細情報.

于 2013-02-25T22:45:08.213 に答える
0

あなたは正しいです。この場合、説明したレースのためにリソースが2回構築される可能性があります。Java 5+ で (明示的なロックなしで) シングルトンを実装する場合は、Java でシングルトン パターンを実装する効率的な方法は何ですか?への回答で説明されているように、enum シングルトンを使用します。.

于 2013-02-25T22:53:25.723 に答える