7

私はいくつかの通知/待機の例を見ていて、これを見つけました。私は同期ブロックが本質的にクリティカルセクションを定義していることを理解していますが、これは競合状態を示していませんか?どの同期ブロックが最初に入力されるかは指定しません。ウェブサイトごとにこの例で競合状態はありますか?もしそうなら、どのように避けることができますか?

public class ThreadA { 
    public static void main(String[] args){ 
     ThreadB b = new ThreadB(); 
     b.start(); 

     synchronized(b){ 
      try{ 
       System.out.println("Waiting for b to complete..."); 
       b.wait(); 
      }catch(InterruptedException e){ 
       e.printStackTrace(); 
      } 

     System.out.println("Total is: " + b.total); 
     } 
    } 
} 

class ThreadB extends Thread { 
    int total; 

    @Override 
    public void run(){ 
     synchronized(this){ 
      for(int i=0; i<100 ; i++){ 
       total += i; 
      } 
      notify(); 
     } 
    } 
} 

出力:

完了するためにBを待っています...

合計は以下のとおりです。はい、それは競合状態4950

+0

はい、理論的には、メインスレッドが 'wait()'を呼び出す前に、新しいスレッドが 'notify()'することができました。 – overthink

+1

'synchronized'ブロックは競合を防止するとは考えないでください。それは単にレースの_スコープ_を制限するだけです。それは、トラックがお互いを追い越すには狭すぎる車である自動車レースコースのセクションのようなものです。彼らはまだレースをしていますが、その1つのセクションを1つのファイルで処理する必要があります。 'synchronized'ブロックでも同じことが言えます:スレッドはここではレース中ではありませんが、ここで_getに競合し、他のレースで競合します。 –

+1

@jameslargeそれは素晴らしいアナロジーでした! 2つのスレッドは同期ブロックに同時に存在することはできませんが、明示的に指定されない限り、最初に誰がそこに到達する可能性がありますか? – trevalexandro

答えて

5

いいえ、最初に実行されるスレッドは保証されません。スレッドbは、メインスレッドが待機を開始する前に通知を行うことができます。

これに加えて、スレッドは通知されずに待機から戻ってくることができるので、技術的に待機に入る前にフラグを設定してチェックするだけでは十分ではありません。 bは関係なく、誰が最初に起動し、その計算で行われるまで、メインスレッドが待機するように、あなたは

public class ThreadA { 
    public static void main(String[] args) throws InterruptedException { 
     ThreadB b = new ThreadB(); 
     b.start(); 

     synchronized(b){ 
      while (!b.isDone()) { 
       System.out.println("Waiting for b to complete..."); 
       b.wait(); 
      } 
      System.out.println("Total is: " + b.total); 
     } 
    } 
} 

class ThreadB extends Thread { 
    int total; 
    private boolean done = false; 

    @Override 
    public void run(){ 
     synchronized(this){ 
      for(int i=0; i<100 ; i++){ 
       total += i; 
      } 
      done = true; 
      notify(); 
     } 
    } 

    public boolean isDone() {return done;} 
} 

のようなものにそれを書き換えることができます。

ところで、APIドキュメントでは、スレッドで同期させないことをお勧めします。 JDKはスレッドで同期して、スレッド#joinを実装します。終了するスレッドは、それに参加するものすべてが受け取るnotifyAllを送信します。ロックを獲得したスレッドからnotifyまたはnotifyAllを呼び出すと、それに参加しているものが早期に返る可能性があります。これの副作用の1つは、通知を削除するとコードが同じように機能することです。

+0

ThreadBから 'notify()'を取り除き、 'wait()'がまだ起きています...:D – ZhongYu

+0

@ bayou.io:はい、スレッドは終了時に通知を送るので、そうです。 –

+0

私はこれをもう一度見ていましたが、理論的にはメインスレッドが待つ前に通知できませんでしたか?私は同時性のためにスリムであることを知っていますが、私がこの間違ったことを考えていたのか不思議でした@NathanHughes – trevalexandro

2

です。 ThreadBが起動してrunメソッドに入り、ThreadAが同期ブロックに入る前に同期しているので、無期限に待機することはありません。しかし、新しいスレッドが実行を開始するのにかかる時間を考えれば、起こることはほとんどありません。

このタイプの状況を処理する最も簡単で最も推奨される方法は、独自の実装を記述するのではなく、Executorによって提供される呼び出し可能/未来を使用することです。

は、以下の規格せずに、この特定のケースを解決するには、次の

  • はThreadBの同期ブロックの終了時に設定ブール「終了」の値を設定してください。
  • synchronizedブロックに入った後でブール値「finished」がtrueの場合、waitを呼び出すべきではありません。
2

はい - どのスレッドがどの同期ブロックを最初に入力するかは競合です。レースのほとんどのシナリオでは、出力と答えは同じになります。ただし、プログラムはデッドロックします。

  1. メインはb.start()を呼び出してすぐにスケジュールを外します。
  2. スレッドBが開始され、同期を開始し、notify()を呼び出します。この場合
  3. 主はその同期ブロックに入り、呼び出しは(待つ)

と呼ばれるスレッドBが(待ちでブロックされ、メインの前に通知するので、メインは)永遠に待ちます。

しかし、これは起こりそうもありませんが、すべてのスレッディングでは、起こることになり、最悪の場合に起こると結論づけてください。

関連する問題