14

2 つのスレッドと 1 つのオブジェクトがあるとします。1 つのスレッドがオブジェクトを割り当てます。

public void assign(MyObject o) {
    myObject = o;
}

別のスレッドがオブジェクトを使用します:

public void use() {
    myObject.use();
}

変数 myObject は volatile として宣言する必要がありますか? いつ揮発性を使用するか、いつ使用しないかを理解しようとしていますが、これは私を困惑させています。2 番目のスレッドが古いオブジェクトへの参照をローカル メモリ キャッシュに保持している可能性はありますか? そうでない場合、なぜですか?

どうもありがとう。

4

6 に答える 6

11

volatile をいつ使用し、いつ使用しないかを理解しようとしています

ほとんどの場合、使用を避ける必要があります。代わりに AtomicReference (または適切な場合は別のアトミッククラス)を使用します。記憶効果は同じで、意図はより明確です。

理解を深めるために、優れたJava Concurrency in Practiceを読むことを強くお勧めします。

于 2010-06-03T16:51:17.427 に答える
6

複雑な技術的な詳細は置いておいて、変数の修飾子volatileとして多かれ少なかれ見ることができます。メソッドまたはブロックへのアクセスを同期したい場合、通常は次のように修飾子を使用します。synchronizedsynchronized

public synchronized void doSomething() {}

変数へのアクセスを「同期」したい場合は、volatile修飾子を使用します。

private volatile SomeObject variable;

バックグラウンドでこれらは異なることを行いますが、効果は同じです。変更は、次にアクセスするスレッドですぐに表示されます。

volatileあなたの特定のケースでは、修飾子に値があるとは思いません。は、オブジェクトを使用するスレッドよりも前にvolatile、オブジェクトを割り当てるスレッドが実行されることを保証しません。それは逆に良いかもしれません。おそらく最初に nullcheck inメソッドを実行したいでしょう。use()

更新:この記事も参照してください:

変数へのアクセスは、それ自体で同期された同期ブロックに囲まれているかのように機能します。2 番目の点で「あたかも振る舞うか」と言うのは、少なくともプログラマにとっては (そしておそらくほとんどの JVM 実装では) 実際のロック オブジェクトが関係していないからです。

于 2010-06-03T16:50:12.547 に答える
4

volatile Java 変数を宣言することは、次のことを意味します。

  • この変数の値は、スレッドローカルにキャッシュされることはありません
  • 変数へのアクセスは、同期ブロックに囲まれているかのように機能します

volatile の典型的かつ最も一般的な使用法は次のとおりです。

public class StoppableThread extends Thread {
  private volatile boolean stop = false;

  public void run() {
    while (!stop) {
      // do work 
    }
  }

  public void stopWork() {
    stop = true;
  }
}
于 2011-11-13T16:23:39.293 に答える
2

この場合、volatile を使用できます。割り当てスレッドで行われた変更が読み取りスレッドに実際に表示されることを保証するために、変数または同様のメカニズム (AtomicReference など) へのアクセスに関する揮発性の同期​​が必要になります。

于 2010-06-03T17:06:52.803 に答える
1

volatileキーワードを理解するのにかなりの時間を費やしました。@alerootは、世界で最も優れた、最も単純な例を挙げていると思います。

これは、ダミーの説明です(私のように:-)):

シナリオ 1:が volatile として宣言されていないと仮定するとstop、特定のスレッドは次のことを行い、次のように「考えます」:

  1. stopWork()が呼び出されます: をに設定するstop必要がありますtrue
  2. ローカル スタックで実行したので、JVM のメイン ヒープを更新する必要があります。
  3. おっと、JVM は CPU で別のスレッドに道を譲るように言ったので、しばらく停止する必要があります...
  4. よし、戻ってきた。これで、メイン ヒープを自分の値で更新できます。更新中...

シナリオ 2: volatileとして宣言しますstop

  1. stopWork()が呼び出されます: をに設定するstop必要がありますtrue
  2. ローカル スタックで実行したので、JVM のメイン ヒープを更新する必要があります。
  3. 申し訳ありませんが、私はしなければなりません (2) 今 - 私はそう言われていますvolatile. 私はもう少しCPUを占有しなければなりません...
  4. メインヒープを更新しています...
  5. OK、完了です。今、私は譲ることができます。

同期はありません。単純なアイデアです...

念のため、すべての変数を宣言しないのはなぜvolatileですか? Scenario2/Step3 のため。これは少し非効率的ですが、それでも通常の同期よりは優れています。

于 2011-11-18T13:49:17.413 に答える
0

ここには紛らわしいコメントがいくつかあります: 明確にするために、 2 つの異なるスレッドが と を呼び出すと仮定すると、コードはそのままでは正しくありません。assign()use()

が存在しない場合volatile、または別の先行発生関係 (たとえば、共通ロックでの同期) への書き込みは、スレッド呼び出しによって認識されることが保証されませmyObjectん。 .assign()use()

はい、volatileこれを修正する 1 つの方法です (これが正しくない動作であると仮定すると、これを気にしないもっともらしい状況があります!)。

myObject「使用」スレッドは、構築時に割り当てられた値や中間値を含む、の「キャッシュされた」値を見ることができるということは正確です(これも他の先行発生ポイントがない場合)。

于 2010-06-05T09:55:09.557 に答える