5

我有时无法让我的程序不死锁。我想我需要添加第三个同步方法释放,它可以用于在调用 ping 后释放另一个线程。代码如下。

// 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

当某些操作需要获得两个不同的锁时,确保没有死锁的唯一方法是确保尝试执行这些操作的每个线程都以相同的顺序获取多个对象上的锁。

要修复死锁,您需要像这样修改代码 - 不漂亮,但它有效。

 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从您的两种方法中删除关键字。除了“名称”之外,没有任何东西不是线程安全的。

否则,在需要之前不要同步。使用同步块,而不是方法。除非需要,否则不要将任何内容放入同步块中。如果可以,在单独的锁定对象上单独同步字段。不要嵌套同步块,但如果您必须始终以相同的顺序嵌套它们;具有同步对象的层次结构。

同步所有方法是保持线程安全的一种简单、方便的技术。但是,当您的线程开始交互时,很容易死锁或只是减慢速度。然后它变得有趣。

于 2012-07-17T17:47:45.663 回答