2017-12-18 3 views
0

こんにちは、私は実際にはJavaの並行処理を読んでいると私は興味深い文がJavaの揮発性、同期、アトミック例

ロックは、視認性とアトミック性の両方を保証できると述べて読み。 volatile変数 変数は可視性のみを保証できます。

変数をvolatileとして宣言すると、他のすべてのスレッドが更新された値を取得している場合、なぜ文のアトミック性が気になるのかを教えてください。counter = counter + 1;

ありがとうございます。

+0

この投稿をチェックしてください。 https://stackoverflow.com/questions/3519664/difference-between-volatile-and-synchronized-in-java –

+1

"なぜcounter = counter + 1;' "のような文では、アトミック性が気になるのですか?あなたのカウンターに正しい値を持たせたいなら、気をつけてください。 – Kayaman

+0

@so_what:あなたが役に立ったら答えをマークしてください –

答えて

2

volatileキーワードの効果は、およそその変数に対する個々の読み取りまたは書き込み操作がアトミックであることにあります。

ただし、1つの読み取りと1つの書き込みを行うi ++と同等の複数の読み取り/書き込みを必要とする操作はアトミックではありません。別のスレッド読み取りと書き込みの間でiに書き込むことができます。

AtomicIntegerおよびAtomicReferenceのようなAtomicクラスは、AtomicIntegerのインクリメントを含む、より広範囲の操作をアトミックに提供します。あなたはカウンターのような文でアトミック気にする必要がある理由です

=カウンタ+ 1

このポストを確認してくださいVolatile Vs Atomic

+0

この回答は、これらの非アトミック操作によって発生した問題を解決する方法を述べているようです。https://docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.htmlしかし、それがなぜ始まるのが問題なのかという疑問に答えることはできません。つまり、変数が2つのスレッドでインクリメントされる場合、両方のスレッドがインクリメントする前に同じ値を読み取ると、インクリメントが1つしか発生しません – Cruncher

+0

"特に、複数の読み込み/書き込みが必要な操作 - i ++、これは、i = i + 1と同じです。これは1つの読み取りと1つの書き込みを行います - アトミックではありません。なぜなら、別のスレッドが読み取りと書き込みの間にiに書き込む可能性があるからです。気に? @Cruncher –

+0

"これとは別のスレッドが私に読書と書込みの間に書いているかもしれません"というステートメントは、私にはデモを行ったので意味がありますが、スレッドセーフについては以下のユースケースを考慮することができます: 1)揮発性+アトミック変数(individulyではない)=完全 2)volatile + synchronizedブロック=完全 3)個々のatmoic操作+ synchronized =完全 –

2

はここで自分自身で揮発性ではないことを証明している実行可能なアプリケーションは、自己完結型の例です十分な。 4つのスレッドはカウンタをそれぞれ10,000回増分するので、最後にカウンタが40,000になると予想されます。これは、プリミティブint変数とAtomicIntを使用し、それぞれ5回試します。

import java.util.Collections; 
import java.util.concurrent.*; 
import java.util.concurrent.atomic.AtomicInteger; 

class AtomicDemo { 
    interface Demo extends Callable<Void> { 
     int getCounter(); 
    } 

    static class UsePrimitive implements Demo { 
     private volatile int counter = 0; 

     public Void call() throws Exception { 
      for (int i = 1; i <= 10000; ++i) { 
       ++counter; 
      } 
      return null; 
     } 

     public int getCounter() { 
      return counter; 
     } 
    } 

    static class UseAtomic implements Demo { 
     final AtomicInteger counter = new AtomicInteger(0); 

     public Void call() throws Exception { 
      for (int i = 1; i <= 10000; ++i) { 
       counter.incrementAndGet(); 
       System.out.print(""); 
      } 
      return null; 
     } 

     public int getCounter() { 
      return counter.get(); 
     } 
    } 

    public static void main(String[] args) throws Exception { 
     ExecutorService exec = Executors.newFixedThreadPool(4); 
     for (int i = 1; i <= 5; ++i) { 
      Demo demo = new UsePrimitive(); 
      exec.invokeAll(Collections.nCopies(4, demo)); 
      System.out.println("Count to 40000 using primitive, attempt number " + i + ": " + demo.getCounter()); 
     } 
     for (int i = 1; i <= 5; ++i) { 
      Demo demo = new UseAtomic(); 
      exec.invokeAll(Collections.nCopies(4, demo)); 
      System.out.println("Count to 40000 using atomic, attempt number " + i + ": " + demo.getCounter()); 
     } 
     exec.shutdownNow(); 
    } 
} 

典型的な出力:

Count to 40000 using primitive, attempt number 1: 39711 
Count to 40000 using primitive, attempt number 2: 39686 
Count to 40000 using primitive, attempt number 3: 39972 
Count to 40000 using primitive, attempt number 4: 39840 
Count to 40000 using primitive, attempt number 5: 39865 
Count to 40000 using atomic, attempt number 1: 40000 
Count to 40000 using atomic, attempt number 2: 40000 
Count to 40000 using atomic, attempt number 3: 40000 
Count to 40000 using atomic, attempt number 4: 40000 
Count to 40000 using atomic, attempt number 5: 40000 

あなたが見る、だけAtomicIntとあなたが常に期待どおりの結果を得るのですか。

+0

素晴らしいです。スレッドが最新の値を取得するためのものであるという結論に至ります。私たちは正しい値を取得しません。また、Atomicxxxクラスを使用しているときに、それらは揮発性変数を使用して最も価値のある値を取得し、次に同期を使用して値を正しく更新します。 –

関連する問題