3

重複の可能性:
Javaでいつ正確にvolatileキーワードを使用しますか?

Javaで揮発性修飾子が必要なのはいつ、そしてなぜですか?

揮発性の変更されたプリミティブまたはオブジェクト参照の実際の使用法を確認することに興味があります。

4

5 に答える 5

10

volatile修飾子は、同時に実行されるスレッドに注意するようにJVMに指示します。基本的に、volatileは、変数の値がさまざまなスレッドによって変更されることを示すために使用されます。

揮発性のJava変数を宣言するということは、次のことを意味します。

  • この変数の値がスレッドローカルにキャッシュされることはありません。すべての読み取りと書き込みは「メインメモリ」に直接送信されます。

  • 変数へのアクセスは、同期されたブロックに囲まれ、それ自体で同期されているかのように機能します。

少なくともプログラマーにとって(そしておそらくほとんどのJVM実装では)実際のロックオブジェクトが関与していないため、2番目のポイントで「あたかも動作する」と言います。


volatile修飾子は、volatileによって変更された変数が、プログラムの他の部分によって予期せず変更される可能性があることをコンパイラーに通知します。これらの状況の1つには、マルチスレッドプログラムが含まれます。

マルチスレッドプログラムでは、2つ以上のスレッドが同じインスタンス変数を共有する場合があります。効率を考慮して、各スレッドはそのような共有変数の独自のプライベートコピーを保持できます。

変数の実際の(またはマスター)コピーは、同期されたメソッドが入力されたときなど、さまざまな時点で更新されます。このアプローチは問題なく機能しますが、非効率的な場合もあります。場合によっては、本当に重要なのは、変数のマスターコピーが常に現在の状態を反映していることです。

これを確実にするには、変数をvolatileとして指定するだけです。これにより、コンパイラは常にvolatile変数のマスターコピーを使用する必要があります(または、少なくとも、プライベートコピーは常にマスターコピーで最新の状態に保つ必要があります。その逆も同様です)。 。また、マスター変数へのアクセスは、プライベートコピーで実行される正確な順序で実行する必要があります。

于 2012-05-01T05:00:16.250 に答える
9

マルチスレッド プログラミングを使用している場合は、volatile キーワードがより便利になります。複数のスレッドが同じ変数を使用する場合、各スレッドはその変数のローカル キャッシュの独自のコピーを持ちます。したがって、値を更新するときは、実際にはメイン変数メモリではなくローカル キャッシュで更新されます。同じ変数を使用している他のスレッドは、別のスレッドによって変更された値について何も知りません。この問題を回避するために、変数を volatile として宣言すると、変数はローカル キャッシュに格納されなくなります。スレッドが値を更新するたびに、メイン メモリに更新されます。したがって、他のスレッドは更新された値にアクセスできます。

変数 volatile 手段の宣言

  • キャッシュが維持されないということは、メイン メモリで行われたすべての変更を意味します。
  • この変数へのアクセスは、同期単位ですが、同期ブロックとして機能します。

例 -

public class Snippet implements Runnable{
volatile int num =0;

public void run(){
    Thread t = Thread.currentThread();
    String name = t.getName();
    if(name.equals("Thread1")){
        num=10;
    }
    else{
        System.out.println("value of num is :"+num);
    }

}
public static void main(String args[]) throws InterruptedException{
    Runnable r = new Snippet();
    Thread t1 = new Thread(r);
    t1.setName("Thread1");
    t1.start();

    Thread.sleep(1000);

    Thread t2 = new Thread(r);
    t2.setName("Thread2");
    t2.start();
}
 }
于 2012-05-01T05:34:10.473 に答える
4

(この回答は、Java 5+ を想定しています。それ以前は、volatile保証が弱かったです。)

フィールドへの書き込みと、別のスレッドによるそのフィールドへの後続の読み取りの間に、正式な「前に発生する」関係とも呼ばれるメモリバリアを確保したい場合に役立ちます。同期は、他のマルチスレッドの保証と同様にその関係も提供しますが、少し遅くなり、同期のボトルネックを作成する可能性があります。

ユース ケースの 1 つは、並列コレクション クラス ( ConcurrentHashMap、 または などLinkedBlockingQueue) であり、アトミックな比較および設定 (CAS) 操作などと組み合わせて、 を使用せずに正しいスレッド セーフ コードを記述できますsynchronized

于 2012-05-01T05:13:11.810 に答える
2

volatile によって変更されたは、プログラムの他の部分によって予期せず変更される可能性があることを にvolatile modifier伝えます。これらの状況の 1 つに、マルチスレッド プログラムが関係しています。 マルチスレッド プログラムでは、2 つ以上のプログラムが同じ を共有することがあります。効率を考慮して、それぞれがそのような共有変数 ( ) の独自のプライベート コピーを保持できます。の実際の (またはマスター) コピー ( ) は、が入力されたときなど、さまざまな時点で更新されます。 このアプローチは問題なく機能しますが、非効率的な場合があります。場合によっては、変数のマスター コピーが常に現在の状態を反映していることだけが重要な場合があります。これを確実に行うには、単純に を指定します。compilervariable
threadsinstance variablethreadin cachein ramvariablesynchronized method
variable as volatilecompiler常に のマスター コピーを使用する必要があるvolatile variable(キャッシュは保持されません)。また、マスター変数へのアクセスは、プライベート コピーで実行される正確な順序で実行する必要があります。
元...

public class snippet1 implements Runnable{
volatile int num =0;

public void run(){
    Thread t = Thread.currentThread();
    String name = t.getName();
    if(name.equals("Thread1")){
        num=10;
    }
    else{
        System.out.println("value of num is :"+num);
    }

}
public static void main(String args[]) throws InterruptedException{
    Runnable r = new snippet1();
    Thread t1 = new Thread(r);
    t1.setName("Thread1");
    t1.start();

    Thread.sleep(1000);

    Thread t2 = new Thread(r);
    t2.setName("Thread2");
    t2.start();
}

}

上記の例では、揮発性を使用しない場合、2 番目のスレッドの出力は 0 になる可能性があります。
ただし、揮発性として配置すると、値が 10 であることが保証されます。

于 2012-05-01T05:20:18.110 に答える
1

あなたは最初の質問に対して良い答えを得ました。2つ目:

誰かが私にそれのリアルタイムのシナリオを教えてもらえますか

IMO、あなたは決してあなたが不安定であってはなりません。マルチスレッドアプリ用のより優れたツールがあります。このような高級言語にこのキーワードがあるのは少し奇妙です。これは良い読み物です(C#についてですが、Javaはこの点で似ています)

于 2012-05-01T05:33:05.360 に答える