请帮助我理解这个死锁示例

时间:2016-09-07 06:53:57

标签: java multithreading deadlock

对于我的编程语言类,我们已经获得了一个简单的Java死锁示例,并被要求解决它。我不想直接得到这个问题的答案,我主要想知道我的理解缺乏的地方。这是代码:

import java.applet.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

// 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 () {
      //System.out.println(m1.getName() + " about to ping " + m2.getName());
      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();
   }
}

当我执行上面的代码时,我相信每次都会发生以下情况(虽然它没有,因为有时我们会死锁):

女孩哄男孩,锁定ping()方法。在ping()内,女孩试图致电boy.confirm()。男孩的confirm()答案,从而将我们带回Girl.ping()完成的地方,取消了ping()的锁定,而男孩的实例完全相同。使用所有锁定,似乎整个程序被序列化,从而破坏了多线程的目的?无论如何,我通常会得到以下输出

Starting...1
Girl (ping): pinging Boy
Boy (confirm): confirm to Girl
Girl (ping): got confirmation
Boy (ping): pinging Girl
Girl (confirm): confirm to Boy
Boy (ping): got confirmation

但有时我们会遇到死锁,输出变为:

Girl (ping): pinging Boy
Boy (ping): pinging Girl

我不明白我们如何才能进入这种状态,因为我们第一次进入时似乎已经锁定了ping()方法,所以Boy怎么可能会调用{{1如果女孩已经在使用它了?当男孩忙着打电话ping()时,女孩是否试图拨打boy.confirm()

2 个答案:

答案 0 :(得分:5)

您的ping方法已同步并锁定this,然后它会继续confirm上的p,因此也会尝试锁定Boy。步骤:

  1. "女孩"线程获取ping对象上的锁定,输入Girl;
  2. "男孩"线程获取ping对象上的锁定,输入Boy.confirm;
  3. 女孩想要致电Girl.confirm,等待锁定;
  4. 男孩想要致电~,等待锁定;
  5. 死锁。

答案 1 :(得分:0)

  • 主题1:开始
  • 主题1:在a上调用ping,上获取显示器,执行System.out.println进行打印 女孩(ping):pinging Boy
  • 线程1:被VM抢占
  • 主题2:开始
  • 主题2:在b上调用ping,在b上获取显示器,执行System.out.println以打印男孩 (ping):pinging Girl
  • 主题2:致电a.confirm,等待监视器
  • 主题1:恢复
  • 主题1:致电b.confirm,等待监视器b
  • 线程1和2现在正在等待另一个线程所在的资源 保持。死锁

这里的问题是public synchronized void ping (Monitor p)表示该类的实例上的监视器。当涉及到关键部分时,两个不同的实例将完全无关。

当不同线程以不同顺序获取同步机制时,通常会发生死锁。要解决这个问题(在练习的上下文中),有两种可能性:

  1. 在类实例上进行同步,这是激进的

    public void ping (Monitor p) {
      synchronized(Monitor.class) {
    
      }
    }
    
  2. 明确同步顺序,这是详细和/或容易出错的。例如

     class Runner extends Thread {
        Monitor m1, m2;
        bool m1_first;
        public Runner (Monitor m1, Monitor m2, bool sync_m1_first) { 
           this.m1 = m1;
           this.m1 = m2;
           m1_first = sync_m1_first;
        }
    
        public void run () {
    
           if(m1_first)
           {
               synchronized(m1) {
                   synchronized(m2) {
                       m1.ping(m2);
                   }
               }
           }
           else
           {
               synchronized(m2) {
                   synchronized(m1) {
                       m1.ping(m2);
                   }
               }
           }
        }
     }
     ...
     (new Runner(a, b, true)).start();
     (new Runner(b, a, false)).start();