5

プログラムが時々デッドロックしないようにするのに問題があります。ping が呼び出された後に他のスレッドを解放するために使用できる、3 つ目の同期メソッド解放を追加する必要があると思います。コードは以下です。

// Attempt at a simple handshake.  Girl pings Boy, gets confirmation.
// Then Boy pings girl, get confirmation.
class Monitor {
    String name;

    public Monitor (String name) { this.name = name; }

    public String getName() {  return this.name; }

     // Girl thread invokes ping, asks Boy to confirm.  But Boy invokes ping,
    // and asks Girl to confirm.  Neither Boy nor Girl can give time to their
    // confirm call because they are stuck in ping.  Hence the handshake 
    // cannot be completed.
    public synchronized void ping (Monitor p) {
      System.out.println(this.name + " (ping): pinging " + p.getName());
      p.confirm(this);
      System.out.println(this.name + " (ping): got confirmation");
    }

    public synchronized void confirm (Monitor p) {
       System.out.println(this.name+" (confirm): confirm to "+p.getName());
     }
}

class Runner extends Thread {
    Monitor m1, m2;

    public Runner (Monitor m1, Monitor m2) { 
      this.m1 = m1; 
      this.m2 = m2; 
    }

    public void run () {  m1.ping(m2);  }
}

public class DeadLock {
    public static void main (String args[]) {
      int i=1;
      System.out.println("Starting..."+(i++));
      Monitor a = new Monitor("Girl");
      Monitor b = new Monitor("Boy");
      (new Runner(a, b)).start();
      (new Runner(b, a)).start();
    }
}
4

2 に答える 2

6

いくつかの操作で 2 つの異なるロックを取得する必要がある場合、デッドロックが発生しないようにする唯一の方法は、それらの操作を実行しようとするすべてのスレッドが複数のオブジェクトのロックを同じ順序で取得するようにすることです。

デッドロックを修正するには、コードを次のように変更する必要があります。きれいではありませんが、機能します。

 public void ping (Monitor p) {
  Monitor one = this;
  Monitor two = p;
  // use some criteria to get a consistent order
  if (System.identityHashCode(one) > System.identityHashCode(two)) {
    //swap
    Monitor temp = one;
    one = two;
    two = one;
  }
  synchronized(one) {
       synchronized(two) {
           System.out.println(this.name + " (ping): pinging " + p.getName());
           p.confirm(this);
           System.out.println(this.name + " (ping): got confirmation");
        }
  }
}
于 2012-07-17T16:17:53.450 に答える
0

これはトリッキーなものです。Monitor.nameを作成するvolatileか、独自のロックオブジェクトと同期します。または、これまでで最高の方法finalです。次にsynchronized、2つの方法からキーワードを捨てます。「名前」以外に、スレッドセーフでないものはありません。

それ以外の場合は、必要になるまで同期しないでください。メソッドではなく、同期されたブロックを使用します。同期ブロックに必要な場合を除いて、同期ブロックには何も入れないでください。可能であれば、フィールドを別々のロックオブジェクトで別々に同期します。同期ブロックをネストしないでください。ただし、常に同じ順序でネストする必要がある場合は、同期オブジェクトの階層があります。

すべてのメソッドを同期することは、スレッドセーフを維持するためのシンプルで手軽なテクニックです。ただし、スレッドが相互作用し始めると、デッドロックが発生したり、速度が低下したりするのは簡単です。それからそれは面白くなります。

于 2012-07-17T17:47:45.663 に答える