首先,here's a sample:
public class Deadlock {
static class Friend {
private final String name;
public Friend(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public synchronized void bow(Friend bower) {
System.out.format("%s: %s has bowed to me!%n",
this.name, bower.getName());
bower.bowBack(this);
}
public synchronized void bowBack(Friend bower) {
System.out.format("%s: %s has bowed back to me!%n",
this.name, bower.getName());
}
}
public static void main(String[] args) {
final Friend alphonse = new Friend("Alphonse");
final Friend gaston = new Friend("Gaston");
new Thread(new Runnable() {
public void run() { alphonse.bow(gaston); }
}).start();
new Thread(new Runnable() {
public void run() { gaston.bow(alphonse); }
}).start();
}
}
我得不到的是 阻塞是如何发生的。 main函数启动两个线程,每个线程都开始自己的弓箭。
'同步'到底有什么阻碍?为同一个对象运行相同的函数(我原来认为)?同一个类的所有对象的相同功能?同一对象的所有同步函数?同一类的所有对象的所有同步函数?
帮助我。
答案 0 :(得分:26)
在Java中,每个Object
都为线程提供synchronize
或锁定它的能力。方法同步时,该方法使用其对象实例作为锁。在您的示例中,方法bow
和bowBack
都是synchronized
,并且两者都在同一个类Friend
中。这意味着执行这些方法的任何线程都将在Friend
实例上同步锁定。
导致死锁的一系列事件是:
alphonse.bow(gaston)
,synchronized
alphonse
对象上的Friend
。这意味着Thread必须从此对象获取锁定。gaston.bow(alphonse)
,synchronized
gaston
对象上的Friend
。这意味着Thread必须从此对象获取锁定。bowback
并等待gaston
上的锁定被释放。bowback
并等待alphonse
上的锁定被释放。更详细地显示事件序列:
main()
开始在主Therad中执行(称之为Thread#1),创建两个Friend
实例。到目前为止,这么好。new Thread(new Runnable() { ...
启动其第一个新线程(称为线程#2)。线程#2在alphonse.bow(gaston)
synchronized
对象上调用alphonse
,Friend
。因此,线程#2获取alphonse
对象的“锁定”并输入bow
方法。gaston.bow(alphonse)
,它在gaston
Friend
对象上同步。由于没有人获得gaston
对象实例的“锁定”,因此线程#3成功获取此锁并输入bow
方法。bower.bowBack(this);
,其中bower
是gaston
实例的引用。这是gaston.bowBack(alphonse)
调用的逻辑等价物。因此,此方法在synchronized
实例上为gaston
。此对象的锁已被获取并由另一个线程(线程#3)保持。因此,线程#2必须等待gaston
上的锁定被释放。线程处于等待状态,允许线程#3进一步执行。bowback
,在这种情况下,它在逻辑上与调用alphonse.bowBack(gaston)
相同。为此,它需要获取alphonse
实例的锁,但此锁由Thread#2保存。此线程现在处于等待状态。现在你处于一个线程都无法执行的位置。线程#2和线程#3都在等待锁定被释放。但是没有Thread可以在没有Thread进展的情况下释放锁。但是如果没有锁被释放,两个线程都无法取得进展。
因此:僵局!
死锁通常取决于发生的特定事件序列,因此很难调试,因为它们很难重现。
答案 1 :(得分:2)
简而言之,它阻止对同一对象的任何同步方法的调用。
答案 2 :(得分:2)
同一对象的所有同步函数。将方法标记为“已同步”非常类似于在该方法的整个内容周围放置“synchronized(this){”块。我不说“相同”的原因是因为我不知道编译器是否发出相同的字节码,但AFAIK定义的运行时效果是相同的。
死锁是一种经典的锁定反转。一个线程锁定alphonse。然后(或同时在多核系统上)另一个线程锁定gaston。这部分要求线程的调度恰好在正确的点处交错。
每个线程(以任何顺序或同时)然后尝试获取已由另一个线程保持的锁,因此每个线程进入休眠状态。在对方释放锁定之前,它们都不会被唤醒,但是在它被唤醒(或被终止)之前都不会释放锁定。
答案 3 :(得分:2)
synchronized方法与将所有这些方法代码封装到
中相同synchronized(this) {
/// code here ...
}
块。
对于给定的对象实例 o ,一次只能有一个线程运行任何 synchronized(o)块。试图嚎叫的每个其他线程,直到运行该块的线程(其上有同步锁)退出该块(放弃锁)。
在你的情况下,当Alphonse开始在线程1中弯曲时发生死锁,从而进入同步块。线程1然后被系统换出,因此线程2可以启动,并且让Gaston鞠躬。但加斯顿还不能退缩,因为它正在同步Alphonse,并且线程1已经拥有锁定。因此它将等待线程1离开该块。然后系统将交换线程1,这将尝试让阿尔方斯退回。除非它不能这样做,因为线程2在Gaston上具有同步锁定。两个线程现在都被卡住了,等待对方完成鞠躬,然后才能退后......