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

Java 请帮助我理解这个死锁示例,java,multithreading,deadlock,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

对于我的编程语言类,我们得到了一个简单的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()
方法。女孩在
ping()
内,试图呼叫
男孩。确认()
。男孩的
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()
方法,所以如果女孩已经在使用它,男孩怎么能调用
ping()
?女孩正试图呼叫男孩。男孩正忙着呼叫时,确认()。
ping()

  • 线程1:开始
  • 线程1:在a上调用ping,在a上获取监视器,
    执行
    进行打印 女孩(平):平男孩
  • 线程1:被VM抢占
  • 线程2:开始
  • 线程2:在b上调用ping,在b上获取监视器,执行
    System.out.println
    以打印男孩 (平):平姑娘
  • 线程2:呼叫a.confirm,等待a上的监视器
  • 线程1:恢复
  • 线程1:呼叫b。确认,等待b上的监视器
  • 线程1和2正在等待资源,而另一个线程正在等待资源 举办。死锁
这里的问题是,
public synchronized void ping(Monitor p)
表示类的实例上的监视器。当涉及到关键部分时,两个不同的实例将是完全不相关的

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

  • 在类实例上同步,这是根本性的

    public void ping (Monitor p) {
      synchronized(Monitor.class) {
    
      }
    }
    
  • 要明确同步顺序,因为同步顺序冗长且容易出错。比如说

     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();
    
    • 线程1:开始
    • 线程1:在a上调用ping,在a上获取监视器,
      执行
      进行打印 女孩(平):平男孩
    • 线程1:被VM抢占
    • 线程2:开始
    • 线程2:在b上调用ping,在b上获取监视器,执行
      System.out.println
      以打印男孩 (平):平姑娘
    • 线程2:呼叫a.confirm,等待a上的监视器
    • 线程1:恢复
    • 线程1:呼叫b。确认,等待b上的监视器
    • 线程1和2正在等待资源,而另一个线程正在等待资源 举办。死锁
    这里的问题是,
    public synchronized void ping(Monitor p)
    表示类的实例上的监视器。当涉及到关键部分时,两个不同的实例将是完全不相关的

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

  • 在类实例上同步,这是根本性的

    public void ping (Monitor p) {
      synchronized(Monitor.class) {
    
      }
    }
    
  • 要明确同步顺序,因为同步顺序冗长且容易出错。比如说

     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();
    

  • 您的
    ping
    方法已同步,并在
    this
    上锁定,然后继续在
    p
    上调用
    confirm
    ,从而尝试也锁定它。分步骤:

  • “女孩”线程在
    Boy
    对象上获取锁,进入
    ping
  • “Boy”线程获取
    Girl
    对象的锁,进入
    ping
  • 女孩想打电话给男孩。确认,等待锁
  • 男孩想打电话给女孩。确认,等待锁
  • 僵局

  • 您的
    ping
    方法已同步,并在
    this
    上锁定,然后继续在
    p
    上调用
    confirm
    ,从而尝试也锁定它。分步骤:

  • “女孩”线程在
    Boy
    对象上获取锁,进入
    ping
  • “Boy”线程获取
    Girl
    对象的锁,进入
    ping
  • 女孩想打电话给男孩。确认,等待锁
  • 男孩想打电话给女孩。确认,等待锁
  • 僵局

  • 您的目标是使程序可序列化。它并不违背多线程的目的。当你有一个资源和多个线程时,没有什么神奇之处,即多个线程不能同时使用资源。你的目标是让你的程序可以序列化。它并不违背多线程的目的。当你有一个资源和多个线程时,没有什么神奇的东西可以实现,即多个线程不能同时使用资源。我不清楚的是我们在另一个实例上获得了锁。谢谢。我不清楚的是我们在另一个实例上获得了锁。谢谢