4

私が理解しているように、変数を揮発性として宣言すると、ローカルキャッシュに保存されません。スレッドが値を更新するたびに、メイン メモリに更新されます。したがって、他のスレッドは更新された値にアクセスできます。

しかし、次のプログラムでは、揮発性変数と非揮発性変数の両方が同じ値を表示しています。

2 番目のスレッドの volatile 変数は更新されません。testValue が変更されない理由を誰でも説明できますか。

class ExampleThread extends Thread {
    private int testValue1;
    private volatile int testValue;
    public ExampleThread(String str){
      super(str);
    }
    public void run() {
    if (getName().equals("Thread 1 "))
    {
        testValue = 10;
        testValue1= 10;
        System.out.println( "Thread 1 testValue1 : " + testValue1);
        System.out.println( "Thread 1 testValue : " + testValue);
    }
    if (getName().equals("Thread 2 "))
    {
        System.out.println( "Thread 2 testValue1 : " + testValue1);
        System.out.println( "Thread 2 testValue : " + testValue);
    }               
}
}

public class VolatileExample {
    public static void main(String args[]) {
        new ExampleThread("Thread 1 ").start();
        new ExampleThread("Thread 2 ").start();
    }
}


output:
Thread 1 testValue1 : 10
Thread 1 testValue : 10
Thread 2 testValue1 : 0
Thread 2 testValue : 0
4

6 に答える 6

7

変数は 1 つのスレッドに限定されるため、変数にアクセスする他のスレッドはありません。したがってvolatile、違いはありません。

それらを宣言するstaticと、それらは異なるスレッド間で共有されます。ただし、それでも揮発性変数と不揮発性変数の違いを観察できない場合があります。Java Concurrency in Practiceの章からの引用。3.1.4:

volatile 変数の可視性効果は、volatile 変数自体の値を超えて拡張されます。スレッド A が揮発性変数に書き込み、続いてスレッド B が同じ変数を読み取ると、揮発性変数に書き込む前に A に表示されていたすべての変数の値が、揮発性変数の読み取り後に B に表示されます。したがって、メモリの可視性の観点からは、volatile 変数の書き込みは同期ブロックの終了に似ており、volatile 変数の読み取りは同期ブロックに入るようなものです。

あなたの場合、コードはたまたま volatile 変数を最初に変更するため、他の変数の更新された値は他のスレッドに表示されない場合があります。ここまでは順調ですね。

ただし、変数を変更した同じスレッドから変数の値を出力しているため、いずれにせよ違いは見られません。

Update2:この修正版を試してください (注: テストしていません):

class ExampleThread extends Thread {
    private static int testValue1;
    private static volatile int testValue;
    private int newValue;

    public ExampleThread(String str, int newValue){
      super(str);
      this.newValue = newValue;
    }
    public void run() {
      for (int i = 0; i < 10; i++) {
        System.out.println(getName() + " testValue1 before update: " + testValue1);
        System.out.println(getName() + " testValue before update: " + testValue);
        testValue = i * newValue;
        testValue1 = i * newValue;
        System.out.println(getName() + " testValue1 after update: " + testValue1);
        System.out.println(getName() + " testValue after update: " + testValue);
        sleep(10);
      }               
    }               
}

public class VolatileExample {
    public static void main(String args[]) {
        new ExampleThread("Thread 1 ", 5).start();
        new ExampleThread("Thread 2 ", 10).start();
    }
}

更新:静的フィールドの可視性について - 再び同じ本から (16.2.3 章):

[...] 静的に初期化されたオブジェクトは、構築中または参照時に明示的な同期を必要としません。ただし、これはas-constructed状態にのみ適用されます。オブジェクトが可変の場合、後続の変更を可視化し、データの破損を回避するために、リーダーとライターの両方で同期が必要です。

于 2010-04-15T11:30:34.923 に答える
3

volatileこれは;とは何の関係もありません。これらは の 2 つの別個のインスタンスでありExampleThread、 と の独自のコピーがtestValue1ありtestValue、これらはインスタンス フィールドです (staticすべてのインスタンス間で「共有」されるクラス変数ではありません)。

于 2010-04-15T11:35:09.377 に答える
2

ExampleThread 1 と ExampleThread 2 は異なるオブジェクトです。

そのうちの 1 つで、両方の int フィールドに 10 を割り当てたため、最初のスレッドの出力が表示されます。

次に、int フィールドに何も割り当てていないため、0 が返されます。

于 2010-04-15T11:31:28.507 に答える
1

testValueはメンバー変数であるため、2つのスレッドは2つの独立したコピーを参照します。volatile2つ以上のスレッドが同じオブジェクトへの参照を持っている場合に関連します。

testValueを静的および揮発性にすると効果があります。ただし、タイミング、スケジューリング、およびキャッシュ戦略に大きく依存しているため、その効果は見られない場合があります(おそらく見られないでしょう)。揮発性物質が欠落していると効果が生じることはめったにないため、このようなバグを見つけるのは非常に困難です。スレッドが値を更新し、2番目のスレッドが値を読み取り、値が2つのスレッドのいずれかのキャッシュに残っている場合にのみ表示されます。

于 2010-04-15T11:29:16.370 に答える
1

静的キーワードを失った可能性がありますか?

于 2010-04-15T11:29:26.337 に答える
1

以下は、2 つのスレッドによってアクセスされる変数を示す例です。スレッドは、スレッドの開始時StarterThreadに変数を設定しますstarted。変数が設定されるのをWaiterThread待ちますstarted

public class Main
{
    static /*volatile*/ boolean started = false;

    private static class StarterThread extends Thread
    {
        public void run()
        {
            started = true;
        }
    }

    private static class WaiterThread extends Thread
    {
        public void run()
        {
            while (!started)
            {
            }
        }
    }

    public static void main(String[] args)
    {
        new StarterThread().start();
        new WaiterThread().start();
    }
}

が揮発性でない場合、 が変数の更新された値を取得するstartedことを保証する同期ポイントはありません。その結果、スレッドは「無期限に」実行される可能性があります。WaiterThreadstartedWaiterThread

于 2010-04-15T12:23:18.870 に答える