web-dev-qa-db-ja.com

なぜvolatileブロックを同期ブロックで使用するのですか?

Javaでいくつかの例を確認しました。コードブロックで同期を行って変数を変更しましたが、その変数はもともとvolatileと宣言されていました。シングルトンクラスの例では、一意のインスタンスを揮発性として、それらはそのインスタンスを初期化するブロックを同期化しました...私の質問は、同期中に揮発性と宣言する理由、なぜ両方を実行する必要があるのですか? ?

public class someClass {
volatile static uniqueInstance = null;

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

前もって感謝します。

42
Dorgham

この場合、最初のチェックが同期されたブロック内にある場合は、それ自体で同期で十分です(ただし、そうではなく、変数が揮発性でない場合、あるスレッドが別のスレッドによって実行された変更を認識しない場合があります)。複数の操作をアトミックに実行する必要があるため、揮発性だけでは十分ではありません。しかし注意してください!ここにあるのは、いわゆるダブルチェックロックです-残念ながら 確実に機能しない の一般的なイディオムです。これはJava 1.6以降に変更されたと思いますが、それでもこの種のコードは危険である可能性があります。

[〜#〜] edit [〜#〜]:変数が揮発性の場合、このコードはJDK 5以降に正しく動作します(以前に書いた6ではありません) )、ただしJDK 1.4以前では期待どおりに動作しません。

19

これはダブルチェックロックを使用します。if(uniqueInstance == null)は同期部分内にないことに注意してください。

uniqueInstanceが揮発性でない場合は、部分的に構築されたオブジェクトで「初期化」され、その一部がsynchronizedブロックで実行されているスレッド以外からは見えない可能性があります。 volatileは、この場合、これを全か無かの操作にします。

同期ブロックがない場合は、2つのスレッドが同時にこのポイントに到達する可能性があります。

if(uniqueInstance == null) {
      uniqueInstance = new someClass(); <---- here

そして、2つのSomeClassオブジェクトを作成しますが、これは目的を達成しません。

厳密に言うと、volatileは必要ありません。メソッドは

public static someClass getInstance() {
    synchronized(FullDictionary.class) {
         if(uniqueInstance == null) {
             uniqueInstance = new someClass();
          }
         return uniqueInstance;
    }
}

ただし、これにより、getInstance()を実行するすべてのスレッドの同期とシリアル化が発生します。

7
nos

この投稿 はvolatileの背後にある考え方を説明しています。

また、精力的な作業 Java Concurrency in Practice でも取り上げられています。

主な考え方は、同時実行性には共有状態の保護だけでなく、スレッド間のその状態の可視性も含まれるということです。これがvolatileが入る場所です(この大きなコントラクトは Javaメモリモデル によって定義されます)。

4
Michael Easter

同期ブロックを使用せずに同期を行うことができます。 volatile変数を使用する必要はありません... volatileは、メインメモリから1つの変数を更新し、同期します。メインメモリからアクセスされたすべての共有変数を更新します。要件に応じて使用できます。

0
user1237385