2016-05-22 2 views
3

私は2つのスレッドを持っており、ある値より小さい間にint変数をインクリメントしたい。私のプログラムはそれを行いますが、2つの異なるスレッドは値を別々にインクリメントしません。
onetwoは二つのスレッドであれば、私は出力がこのスレッドを使用してintをインクリメントする

one 1 
two 2 
one 3 
two 4 
one 5 
two 6 
one 7 
two 8... 

のようになりたいがここに私のプログラムです:

class IncTo100Demo implements Runnable { 
    public volatile int count = 0; 

    public void run() { 
     Thread current = Thread.currentThread(); 
     try { 
      while(count < 21) { 
       System.out.println(current.getName() + " count = " + count++); 
       current.sleep(1000); 
      } 
     } 
     catch(Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    public static void main(String[] args) { 
     Thread one = new Thread(new IncTo100Demo(), "one"); 
     Thread two = new Thread(new IncTo100Demo(), "two"); 

     try { 
      one.start(); 
      two.start(); 
      System.out.println("one alive? " + one.isAlive()); 
      one.join(); 
     } 
     catch(Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 

その出力は次のとおりです。

one alive? true 
two count = 0 
one count = 0 
two count = 1 
one count = 1 
two count = 2 
one count = 2 
two count = 3 
one count = 3 
two count = 4 
one count = 4 
two count = 5 
one count = 5 
two count = 6 
one count = 6 
two count = 7 
one count = 7 
two count = 8 
one count = 8 
two count = 9 

方法これらのスレッドで変数を個別に更新できるようにして、出力を私が望むように見せることができますか?

+0

メソッドでsynchronizedキーワードを使用します。 –

答えて

4

countは、クラスIncTo100Demoのインスタンス変数です。それぞれのスレッドは、そのクラスの独自のインスタンスを使用するため、その変数の独自のコピーで動作します。そのため、値を個別に更新するのはこのためです。スレッドを共有するには、クラスの同じインスタンスを使用するか、countを静的にする必要があります。

さらに、2つのスレッドが同じcount変数にアクセスすると、その変数をvolatileとすることで、必要な動作を保証するには不十分です。その動作を確認するには、かなりがかなりありますが、一方のスレッドがもう一方の変数の更新を見逃す可能性があります。これは、式count++を評価し、その副作用を適用することはアトミックではないためです。したがって、両方のスレッドが同じ値のcountを読み取る(つまり、どちらか一方が更新された値を書き込む前に)ことが可能です。その場合、両方とも同じ更新値を計算し、両方ともそれを書きます。結果はあなたが提示した誤った出力と似ていますが、(非常に)時折起こり、一貫していない可能性があります。

この問題を回避するには、countへのアクセスを同期させる必要があります(これは揮発性である必要はありません)。ただし、run()メソッドを同期させることでそれを行うことはできませんが、その場合はrun()が呼び出されたオブジェクトのモニターを使用してメソッドを同期させることになります。あなたのスレッドが同じIncTo100Demoインスタンスを使用していれば、並行性が妨げられます。そうでなければ、それは有効ではありません。

代わりにrun()の内部に同期ブロックを使用してください。

synchronized (this) { 
    // ... read and manipulate 'count' 
} 

そうでない場合は、あなたがスレッド間で共有別のオブジェクトを作成する必要がありますし、その上で同期:2つのスレッドが(私はお勧めします)同じIncTo100Demoインスタンスを共有する場合、あなたはそのインスタンス上で同期することができます。各ループ反復でcountへのすべてのアクセスが同じ同期ブロック(count < 21テストを含む)内にあり、sleep()がその外側にあることに注意してください。同期の範囲を可能な限り狭くします。

これは教育的な演習のようですので、私は詳細を残しておきますが、このヒントをお伝えします。おそらくループを再構成する必要があります。

+0

ジョンの説明をありがとう。スレッドを扱うときに常に 'synchronize'を使うべきですか? これは私が出会ったばかりの練習問題です。私はスレッドについてもっと学びたいと思っています。あなたの情報はかなり役に立ちます。私は 'static'を使っていました。 – strider14

+1

スレッド間でデータを共有する場合は、その共有データへのアクセスを適切に同期させる必要があります。状況によっては、データのvolatile宣言、アトミックなデータ型や 'java.util.concurrent'の他のクラスの使用で十分ですが、時には直接的に同期することが最高ですが、おそらく唯一合理的な方法です。 –

関連する問題