2016-04-26 15 views
1

私は話題がたくさんある知っているスタックオーバーフローで提供されていますが、私はvolatileキーワードについて学ぶしようとしているとして、理論的には、私は私にはその明確なと考えていますが、私は、次の例を実行しようとしたとき、私の理解が失敗したか、私れますシーケンスを視覚化できません。スレッドの同期やvolatileキーワード

volatile-の理解>Making variable as volatile for an instance, makes it non cacheable for threads that means whichever thread is accessing the volatile variable it has to flush the changes into the main memory immediately so that changes should be visible to other thread.

しかし、あなたは出力を参照してください場合は、この例では、スレッドAは6は、スレッドBが、これはそれが必要変わる見たときに、それはそう、メインメモリにフラッシュされるべき数などの揮発性のあるものとして値を書き込みます変更はなく、スレッドBが、私はスレッドあなたがスレッドの実行が起こるか視覚化するために私を助けてくださいすることができますを視覚化することはできませんよ2の代わりに、6または7

を示している参照してください。

public class PerfectExampleToUnderstandVolatile { 
    public static void main(String[] args) { 

     ThreadSample sample = new ThreadSample(); 
     Thread thread = new Thread(sample,"threadA"); 
     Thread thread1 = new Thread(sample,"threadB"); 
     Thread thread2 = new Thread(sample,"threadC"); 
     thread.start(); 
     thread1.start(); 
     thread2.start(); 
    } 
} 

class ThreadSample implements Runnable { 
     volatile int count; 

    public ThreadSample() { 

    } 

    @Override 
    public void run() { 
     while (count < 15) { 
      System.out.println(Thread.currentThread().getName() + " " + count); 
      count++; 
     } 

    } 

} 

出力

**threadA 0** 
threadA 1 
threadA 2 
threadA 3 
threadA 4 
threadA 5 
**threadA 6** at this point thread A writes 6 
**threadB 0** so here thread B should show 6 
threadA 7 
**threadC 6** same should be here 
threadC 9 
threadA 10 
threadB 11 
threadB 13 
threadB 14 
threadA 12 
threadC 11 

答えて

7

出力には矛盾がありません:、文字列を構築し、そのカウントが0であることがわかりを開始することができます

  • スレッドBが「threadB 0 ""と "スリープ"に移動します。彼が目覚めたときと同じ正確に行うことができます

  • スレッドC(実際のカウント値がその瞬間で6であるが)その後、彼はそれがコンソールに出力します。また6 0の代わり

、インクリメント操作count++カウント、アトミックではありません++なぜあなたがすべき場合には

int temp = count + 1; 
count = temp; 

に等しい2つのスレッドが同時にカウントをインクリメントした場合、それは値が1だけではなく、2つインクリメントされることも可能であるだこと揮発性変数にインクリメント演算を使用しないで、代わりにAtomicIntegerを使用してください。

+1

読み書きが同期されているからといって、出力が同期されているだけであるからといって、あなたが言うのは道徳的だと言えるでしょう。 –

+0

SysoutはやはりIo操作なので、基本的に他のスレッドが引き継ぐコンソールに書き込む前です。 –

+0

@youtubefreakより正確には、問題は読み書き操作と出力がアトミックではないことです。 – AdamSkywalker

0

揮発性は、すべてがvolatile変数の読み取りというvolatileキーワードの保証は、メインメモリから直接読み込まれ、すべてがvolatile変数への書き込みがあり、メインメモリに直接書き込まれている場合でも、必ず

十分ではありません変数volatileを宣言するだけでは不十分な状況です。

実際には、複数のスレッドでも共有揮発性変数に書き込むことができ、および変数に書かれた新しい値が以前の値に依存していない場合は、まだ、メインメモリに保存された正しい値を持っています。言い換えれば、共有volatile変数に値を書き込んだスレッドが、その値を読み取って次の値を把握する必要がない場合。

上記の例のようにvolatile変数の値が最初に読み取られ、その値に基づいて共有volatile変数に新しい値が生成されると、volatile変数は十分でなくなり次第正確な可視性を保証する。揮発性変数の読み取りと新しい値の書き込みとの間の短い時間間隔は、競合状態を作成し、複数のスレッドがvolatile変数の同じ値を読み取り、変数の新しい値を生成し、書き込み時にメインメモリに戻った値 - お互いの値を上書きします。

複数のスレッドが同じカウンタをインクリメントしている状況は、まさに揮発性変数では不十分な状況です。スレッド間のスレッドスケジューリングと

0
  1. 問題は、(するSystem.out.printlnを呼び出す)カウンタを増加させるコンソール他のスレッド上のカウンタ値と値プリントと

  2. を実行することができあなたのループではなく、17回実行同期の欠如のために15の。 揮発性の保証は可視性のみを保証し、同期は可視性と相互排除を保証します。複合アクション(読み込み増分書き込み)であるcount ++を実行しているときに、同期を使用し、AdamSkywalkerがこのシナリオであなたの友人であることを示唆したように、

関連する問題