请帮助我理解这个死锁示例
Please help me understand this deadlock example
对于我的编程语言 class 我们已经给出了一个简单的 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();
}
}
当我执行上面的代码时,我相信每次都应该发生以下情况(尽管它不会发生,因为有时我们会陷入僵局):
Girl ping Boy,锁定 ping()
方法。 ping()
里面的女孩试图打电话给 boy.confirm()
。 Boy 的 confirm()
答案,从而将我们带回到 Girl.ping()
它结束的地方,解除了对 ping()
的锁定,而 Boy 的实例做了完全相同的事情。有了所有的锁,似乎整个程序都被序列化了,但因此违背了多线程的目的?无论如何,我通常会得到以下输出
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 怎么可能调用 ping()
如果女孩已经在使用它?当男孩忙于呼叫 ping()
时,女孩是否正在尝试呼叫 boy.confirm()
?
- 线程 1:开始
- 线程1:在a上调用ping,在a上获取监视器,执行
System.out.println
打印
女孩 (ping):ping 男孩
- 线程 1:被 VM 抢占
- 线程 2:开始
- 线程 2 : 在 b 上调用 ping,在 b 上获取监视器 ,执行
System.out.println
打印 Boy
(ping): ping 女孩
- 线程 2:调用 a.confirm,在 .
上的 监视器上等待
- 线程 1:恢复
- 线程 1:调用 b.confirm,等待 b
上的监视器
- 线程 1 和 2 现在正在等待另一个线程正在使用的资源
保持。死锁
这里的意思是 public synchronized void ping (Monitor p)
表示 class 的 实例 上的监视器。当涉及到关键部分时,两个不同的实例将完全无关。
当不同线程以不同顺序获取同步机制时,经常会发生死锁。要解决此问题(在练习的上下文中),有两种可能性:
在 class 实例上同步,这是激进的
public void ping (Monitor p) {
synchronized(Monitor.class) {
}
}
明确同步顺序,这很冗长 and\or 容易出错。例如
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
,从而也尝试获取其锁.步骤:
- "Girl" 线程获取
Boy
对象上的锁,进入 ping
;
- "Boy" 线程获取
Girl
对象上的锁,进入 ping
;
- 女孩想打电话给
Boy.confirm
,等待锁定;
- 男孩想调用
Girl.confirm
,等待锁;
- 死锁。
对于我的编程语言 class 我们已经给出了一个简单的 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();
}
}
当我执行上面的代码时,我相信每次都应该发生以下情况(尽管它不会发生,因为有时我们会陷入僵局):
Girl ping Boy,锁定 ping()
方法。 ping()
里面的女孩试图打电话给 boy.confirm()
。 Boy 的 confirm()
答案,从而将我们带回到 Girl.ping()
它结束的地方,解除了对 ping()
的锁定,而 Boy 的实例做了完全相同的事情。有了所有的锁,似乎整个程序都被序列化了,但因此违背了多线程的目的?无论如何,我通常会得到以下输出
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 怎么可能调用 ping()
如果女孩已经在使用它?当男孩忙于呼叫 ping()
时,女孩是否正在尝试呼叫 boy.confirm()
?
- 线程 1:开始
- 线程1:在a上调用ping,在a上获取监视器,执行
System.out.println
打印 女孩 (ping):ping 男孩 - 线程 1:被 VM 抢占
- 线程 2:开始
- 线程 2 : 在 b 上调用 ping,在 b 上获取监视器 ,执行
System.out.println
打印 Boy (ping): ping 女孩 - 线程 2:调用 a.confirm,在 . 上的 监视器上等待
- 线程 1:恢复
- 线程 1:调用 b.confirm,等待 b 上的监视器
- 线程 1 和 2 现在正在等待另一个线程正在使用的资源 保持。死锁
这里的意思是 public synchronized void ping (Monitor p)
表示 class 的 实例 上的监视器。当涉及到关键部分时,两个不同的实例将完全无关。
当不同线程以不同顺序获取同步机制时,经常会发生死锁。要解决此问题(在练习的上下文中),有两种可能性:
在 class 实例上同步,这是激进的
public void ping (Monitor p) { synchronized(Monitor.class) { } }
明确同步顺序,这很冗长 and\or 容易出错。例如
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
,从而也尝试获取其锁.步骤:
- "Girl" 线程获取
Boy
对象上的锁,进入ping
; - "Boy" 线程获取
Girl
对象上的锁,进入ping
; - 女孩想打电话给
Boy.confirm
,等待锁定; - 男孩想调用
Girl.confirm
,等待锁; - 死锁。