32

volatileJavaでは、キーワードが変数の可視性を提供することを理解しています。問題は、変数が可変オブジェクトへの参照である場合、volatileそのオブジェクト内のメンバーにも可視性を提供するかどうかです。

volatile Mutable m以下の例では、複数のスレッドがアクセスして変更している場合、正しく機能しvalueますか?

class Mutable {
    private int value;
    public int get()
    {
        return a;
    }
    public int set(int value)
    {
        this.value = value;
    }
}

class Test {
    public volatile Mutable m;
}
4

5 に答える 5

17

これは、volatile の詳細の一部に関する補足的な説明です。コメントするには長すぎるのでここに書きます。volatile が可視性に与える影響と、それが jdk 1.5 でどのように変化したかを示す例をいくつか挙げたいと思います。

次のコード例を考えます。

public class MyClass
{
  private int _n;
  private volatile int _volN;

  public void setN(int i) {
    _n = i;
  }
  public void setVolN(int i) {
    _volN = i;
  }
  public int getN() { 
    return _n; 
  }
  public int getVolN() { 
    return _volN; 
  }

  public static void main() {
    final MyClass mc = new MyClass();

    Thread t1 = new Thread() {
      public void run() {
        mc.setN(5);
        mc.setVolN(5);
      }
    };

    Thread t2 = new Thread() {
      public void run() {
        int volN = mc.getVolN();
        int n = mc.getN();
        System.out.println("Read: " + volN + ", " + n);
      }
    };

    t1.start();
    t2.start();
  }
}

このテスト コードの動作は jdk1.5+ で明確に定義されていますが、jdk1.5 以前では明確に定義されていません

jdk1.5 より前の世界では、揮発性アクセスと不揮発性アクセスの間に定義された関係はありませんでした。したがって、このプログラムの出力は次のようになります。

  1. 読む: 0, 0
  2. 読み取り: 0、5
  3. 読む: 5, 0
  4. 読む: 5, 5

jdk1.5+ の世界では、volatile のセマンティクスが変更されたため、volatile アクセスは同期とまったく同じ方法で non-volatile アクセスに影響を与えました。したがって、jdk1.5+ の世界では特定の出力のみが可能です。

  1. 読む: 0, 0
  2. 読み取り: 0、5
  3. 読み取り: 5, 0 <- 不可能
  4. 読む: 5, 5

出力 3. は、volatile _volN からの「5」の読み取りによって 2 つのスレッド間の同期ポイントが確立されるため、不可能です。つまり、_volN への割り当ての前に行われた t1 からのすべてのアクションは、 t2 から見える必要があります。

参考文献:

于 2011-01-07T18:54:31.247 に答える
8

あなたの例では、volatileキーワードは、任意のスレッドによって 'm' に書き込まれた最後の参照が、その後 'm' を読み取る任意のスレッドに表示されることを保証するだけです。

get()については何も保証しません。

したがって、次のシーケンスを使用します。

Thread-1: get()     returns 2
Thread-2: set(3)
Thread-1: get()    

3 ではなく 2 を返すことは完全に正当です 。それはvolatile何も変更しません。

ただし、Mutableクラスを次のように変更すると:

class Mutable {
    private volatile int value;
    public int get()
    {
        return a;
    }
    public int set(int value)
    {
        this.value = value;
    }
}

get()次に、Thread-1の 2 番目が 3 を返すことが保証されます。

ただし、volatile通常は最適な同期方法ではないことに注意してください。

単純な get/set の例 (単なる例であることはわかっています)AtomicIntegerでは、適切な同期を使用し、実際に有用なメソッドを提供する のようなクラスの方が優れています。

于 2011-01-06T11:33:05.843 に答える
5

volatileそのように宣言されたオブジェクトへの参照に関する保証のみを提供します。そのインスタンスのメンバーは同期されません。

ウィキペディアによると、次のものがあります。

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

したがって、基本的には、 field を宣言することでvolatile、それと対話することで「同期点」が作成され、その後、変更が他のスレッドで表示されます。しかし、その後、get()orを使用するset()と非同期になります。Java Specには、より完全な説明があります。

于 2011-01-06T11:26:30.707 に答える
0

volatile「可視性を提供」しません。その唯一の効果は、プロセッサーによる変数のキャッシングを防止することです。これにより、同時読み取りおよび書き込みに先行発生関係が提供されます。オブジェクトのメンバーには影響せず、同期 synchronizedロックも提供しません。

コードの「正しい」動作が何であるかを教えていないため、質問に答えることができません。

于 2011-01-06T11:32:38.820 に答える
0

volatile完全な値ではなくの使用は、synchronized本質的に最適化です。volatile最適化は、アクセスと比較して値に対して提供される保証が弱いことに起因しsynchronizedます。時期尚早の最適化は諸悪の根源です。この場合、競合状態などの形になるため、悪を追跡するのが難しい可能性があります。したがって、質問する必要がある場合は、おそらく使用しないでください。

于 2011-01-06T12:56:00.623 に答える