2017-01-14 4 views
2

このプログラムを実行すると、結果が変わります。誰かが私に説明したり、コード内で何が起こっているのかを理解するために答えを見つけることができるトピックを教えてくれますか?Javaでのマルチスレッドの問題点、ランタイムでの異なる結果

class IntCell { 
    private int n = 0; 
    public int getN() {return n;} 
    public void setN(int n) {this.n = n;} 
} 

public class Count extends Thread { 
    static IntCell n = new IntCell(); 

    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()); 
    } 
} 
+1

[_very beginning_](https://docs.oracle.com/cd/E19455-01/806-5257/6je9h032e/index.html)から始め、Javaの並行性について学ぶ必要があると思います。このコードは、ランダムな結果を生成するように設計されているようです。メモリの障壁がなければ並行性は予測できないという基本的な点です。要するに、このコードがランダムな回答を生成する理由を説明することは、あなたを助けることはまずありません。同時コードを書く前に**多くの読書をする必要があります。 –

答えて

0

あなたは、あなたの答えは常に異なっている理由です、並列に2つのスレッドを実行し、これらの2つのスレッドで共有変数を更新しています。このような共有変数を更新するのは良い習慣ではありません。

は、あなたが最初にあなたが同時に2つのスレッドでnは同じ番号を変更 Multithreading、その後 notify and waitsimple cases

0

を理解しておく必要があり、理解するために。 Thread1がn = 2を読み込んだ場合、Thread2はインクリメントを書き込む前にn = 2を読み込み、Thread1はnを3に増やしますが、Thread2はこれ以上増加しませんが、nに別の「3」を書き込みます。スレッド2が読み込まれる前にスレッド1のインクリメントが完了すると、両方ともインクリメントされます。

現在、両方のスレッドが並行しているため、どのCPUサイクルがどのスレッドになるかを決して知ることはできません。これはあなたのマシン上で実行されているものによって異なります。したがって、上記の上書きの状況によって、異なる数のインクリメントが失われます。

これを解決するには、n ++を使用してnに対して実数のインクリメントを実行します。それらは単一のCPUサイクルに入ります。

1

IntCell n静的変数へのアクセスは、あなたの二つのスレッド間の同時です:

static IntCell n = new IntCell(); 

public void run() { 
    int temp; 
    for (int i = 0; i < 200000; i++) { 
    temp = n.getN(); 
    n.setN(temp + 1); 
    } 
} 

レース条件は、それが以前に呼び出されたスレッドに依存するn.setN(temp + 1);が行われたときに予測可能な動作を持つことができないことを確認:temp = n.getN();
現在のスレッドの場合は、スレッドによって値が設定されます。そうでない場合は、最後の値が他のスレッドによって設定されます。

予期しない動作の問題を回避するために、同期メカニズムを追加できます。

4

理由は簡単です:あなたのコードが競合状態の問題になりやすいように、カウンターを原子的に取得および変更しないでください。ここ

は問題示す例である。

  1. スレッド#1コールn.getN()取得0
  2. スレッド#2コールn.getN()取得1コールn.setN(1)1
  3. nを設定する 0
  4. スレッド#
  5. スレッド#2はスレッド#1が既にn1に設定されていることを認識していませんo n.setN(1)を依頼して、の代わりにn1を設定すると、競合状態の問題と呼ばれます。

あなたの最終結果は、その後競合状態の問題の合計量に依存するであろう、それは一つのテストから別のものに変わるので、予測不可能である、あなたのコードを実行中に出会いました。それを修正するために

一つの方法として、次のアトミックそれを行うために、​​ブロックにあなたのカウンタを取得し、設定することで、確かにそれは前nに割り当てられIntCellのインスタンスの排他的ロックを獲得するためにスレッドを強制されますこのコードセクションを実行することができます。

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

出力:

The value of n is 400000 

また、アトミックあなたのカウンタをインクリメントするタイプaddAndGet(int delta)またはincrementAndGet()の方法に依存しているために、あなたのカウンタのAtomicInteger代わりのintを使用することを検討できます。

関連する問題