2017-11-27 4 views
1

私はJavaのスレッドの通信をwait()のnotify()で学習し、いくつかのばかげたことが起こります:
ここには、人間を設定して、 、結果は(ジャック・オス、メアリー女性、ジャックオス、メアリーの女性......)ここ
が私のコードであることを意味している:私はHumanDemoを実行するとなぜ私のJava同期メソッドでこの間違いが起こっていますか?

class Human { 
    private String name; 
    private String sex; 
    private boolean b = false; 

    public synchronized void set(String name, String sex) { 
    if (b) { 
     try { 
      this.wait(); 
     } catch (InterruptedException e) {} 
    } 
    this.name = name; 
    this.sex = sex; 
    b = true; 
    this.notify();   
    }  
    public synchronized void get() { 
    if (!b) { 
     try { 
      this.wait(); 
     } catch (InterruptedException e) {} 
    } 
    System.out.println(name+" "+sex); 
    b = false; 
    this.notify();   
    } 
} 
class SetHuman implements Runnable { 
    private Human h; 
    SetHuman(Human h) { 
    this.h = h; 
    } 
    public void run() {  
    int x = 0; 
    while(true) {   
     if (x==0) { 
      h.set("Jack","male"); 
     } else { 
      h.set("Mary","female"); 
     } 
     x = (x+1)%2; 
    } 
    } 
} 
class GetHuman implements Runnable { 
    private Human h; 
    GetHuman(Human h) { 
    this.h = h; 
    } 
    public void run() { 
    while (true) { 
     h.get(); 
    } 
    } 
} 

class HumanDemo { 
    public static void main(String[]args) { 
    Human h = new Human(); 
    SetHuman sh = new SetHuman(h); 
    GetHuman gh = new GetHuman(h); 

    Thread t1 = new Thread(sh); 
    Thread t2 = new Thread(gh); 

    t1.start(); 
    t2.start(); 
    } 
} 

、それが働いた:result

次に、私の同期関数set()とget()にelse判定を追加して、このことがうまくいった:

public synchronized void set(String name, String sex) { 
    if (b) { 
     try { 
      this.wait(); 
     } catch (InterruptedException e) {} 
    } else { 
     this.name = name; 
     this.sex = sex; 
     b = true; 
     this.notify(); 
    } 
}  
public synchronized void get() { 
    if (!b) { 
     try { 
      this.wait(); 
     } catch (InterruptedException e) {} 
    } else { 
     System.out.println(name+" "+sex); 
     b = false; 
     this.notify(); 
    } 
} 

new result

これはなぜでしょうか?誰でも私に教えてください?ありがとう^ - ^!

+0

イメージを使用せずに問題を説明できますか?これはわかりません – AxelH

+0

ああ、申し訳ありませんが、私はこれらの2人の人間を1人で(ジャック男性、メア女性、ジャック男性、メアリー女性....)印刷する必要があることを意味する最初のものは働いたが、私は他の判断を追加した後、同期メソッドに問題があり、同期されません。 –

答えて

1

作業の最初の例では、set/get()メソッドは相互に排他的です。 this.wait()は、彼らが仕事をする前に他の人を待つように強制します。

2番目の例では、ロックが解放された後にスレッドが 'this'にロックを取得する保証がないため、これをelseとブレークします。そうすれば、任意の数の 'set()'が 'this.name'と 'this.sex'に値を設定しない待機状態に失われる可能性があります。

例(GT = GET ST =設定スレッドをスレッド):

メイン方法は、ST

STスレッド開始:h.set( "ジャック"、 "男性")。 bはfalseです - > this.name = "Jack"; b =真; (今度は、stが再び実行されるか、gtがすでに作成されていて、同期get()メソッドを介して 'this'のロックを取得するかどうかは保証されません。 main-methodはスレッドgtを開始しますか? (後であるかもしれない)

st:h.set( "Mary"、 "female"); bは真です - > this.wait(); (これは誰かが 'this'のロックを解除するまで待っています。このthis.nameとthis.sexはelseステートメントに設定されているので、現在の値は決して設定されず、 "Marry"と "female"次のgtが実行されます)。

?main-method starts thread gt? (既に発生している可能性があります)

gt:bはtrueです - > System.out.println(name + "" + sex); b = false ...(メソッドの終わりでgtが 'this'のロックを解放するようになりました。stは待機状態を残してロックを取得しようとします。gtは 'this'のロックを取得しようとしています。 。。再びここでもスレッドがロックを取得し、今で実行できる保証がない)

かいつまん:

あなたはセットであるため、他のセットの呼び出しの「ランダム」量を離れて投げています方法(交互に起こる可能性は低いです)。 setメソッドでelseがなければ、それはうまくいくはずです。これは、メソッドを呼び出すことを浪費しますが、待機状態になってすぐに実行され、処理が行われずに戻ります。

1

wait()への呼び出しは、the documentationで説明されているように、常にwhileループでなければなりません。あなたはそれをしている、それは良いです。しかし、あなたは間違って得た部分は、whileループをスレッドセーフにするために内部​​ブロックする必要があるということである。

while (!condition) { 
    // Wrong --- another thread might call notify or notifyAll 
    // when the program is at this point, where this thread 
    // will not detect it. 
    synchronized (h) { 
     h.wait(); 
    } 
} 

whileループは常にスレッドの安全性を確保するために、同期ブロックの内部でなければなりません。

synchronized (h) { 
    while (!condition) { 
     h.wait(); 
    } 
} 

また、割り込みは、スレッドが誰かが終了したいと思うシグナルです。それを無視しないでください。最後に

try { 
    synchronized (h) { 
     while (!condition) { 
      h.wait(); 
     } 
    } 
} catch (InterruptedException e) { 
    e.printStackTrace(); 
} 

、これまでに空catchブロックを書くことはありません:

が圧倒的にこれを行う最も簡単な方法は、InterruptedExceptionあるが、自動的にループを終了しますので、のtry/catch内のあなたの全体のループを配置することです。例外を隠すと、コードのトラブルシューティングが非常に困難になります。

関連する問題