2016-05-28 5 views
1

結果が400 000ではないのだろうかと疑問に思っています。なぜ2つのスレッドがブロックされるのですか?Javaスレッドの問題?

class IntCell { 
    private int n = 0; 
    public int getN() {return n;} 
    public void setN(int n) {this.n = n;} 
} 
class Count extends Thread { 
    private static IntCell n = new IntCell(); 
    @Override public void run() { 
     int temp; 
     for (int i = 0; i < 200000; i++) { 
      temp = n.getN(); 
      n.setN(temp + 1); 
     } 
    } 
    public static void main(String[] args) { 
     Count p = new Count(); 
     Count q = new Count(); 
     p.start(); 
     q.start(); 
     try { p.join(); q.join(); } 
     catch (InterruptedException e) { } 
     System.out.println("The value of n is " + n.getN()); 
    } 
} 

なぜそれに問題がありますか?あなたは、変数をインクリメントする方法はアトミック操作ではありませんので

+3

「なぜブロックされますか」とはどういう意味ですか?何が問題なのか - 両方のスレッドが特定の値(たとえば100)をフェッチし、両方ともインクリメント(101に)してから値を格納するとします。 2つの増分が発生しましたが、結果は102の代わりに101になります。 –

+0

結果を追加したい場合があります。 FYI:これはスレッド化と値の同時更新で理解する必要がある基本的なことです。 –

+0

私はあなたに行くとマルチスレッドの仕組みを読むことをお勧めします。あなたの質問は深刻な誤解を意味します。マルチスレッドは非常に難しいので、始める前に理解しておく必要があります。 – davmac

答えて

2

、それらは互いに干渉し、結果は決定的ではありません。たとえば、pnの値を読み取り、たとえば0を取得したとすると、qは同じ値を読み取り、0も取得すると、は値を1に設定し、qも値を1に設定します(それでも、値0)。 nの値は、両方のカウンタが1回 "インクリメント"しても、1だけ増加します。カウンタが互いに干渉しないようにするには、ブロック​​を使用する必要があります。詳細については、https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.htmlを参照してください。

6

確かにそれにあなたをインクリメントします

  1. 新しい価値
に設定し、この値
  • に1を加える前の値を取得します。

    3つの操作が原子的に行われていない場合は、代わりに​​ブロックを使用するか、代わりにAtomicIntegerを使用してください。あなたは次のようにコードを書き換える必要がありますAtomicInteger

    synchronized (n) { 
        temp = n.getN(); 
        n.setN(temp + 1); 
    } 
    

    :それはようなものになるだろう​​ブロックで

    class IntCell { 
        private final AtomicInteger n = new AtomicInteger(); 
        public int getN() {return n.get();} 
        public void incrementN(int n) {this.n.addAndGet(n);} 
    } 
    
    for (int i = 0; i < 200000; i++) { 
        n.incrementN(1); 
    } 
    

    アプローチAtomicIntegerとはとても非ブロッキングでありますそれはより速くなります

  • 2

    ここでの問題は競争条件を許すことです。ループ内のブロックを考えてみましょう:

    temp = n.getN(); 
    n.setN(temp + 1); 
    

    あなたが現在Nを取得し、時間によって、あなたは「古い」値を設定すること、それをインクリメントする時間との間のコードのコンテキストスイッチ。これを回避する一つの方法は、ループの内側部分は、同期ブロックで実行を保証することである:2つのスレッドが同時に一つのオブジェクトにアクセスするとき

    for (int i = 0; i < 200000; i++) { 
        synchronized (n) {/Here! 
         temp = n.getN(); 
         n.setN(temp + 1); 
        } 
    }