这是来自Java文档的死锁教程。我不明白线程被阻塞的地方。由于同步,我认为只有一个线程会进入弓。但是他们都鞠躬了[一个人等待[但是什么时候?]]

那问题在哪里呢?

当我添加注释[打印要跟踪的语句]时。没有僵局。怎么样?

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() {
            @Override
            public void run() {
               // System.out.println("Thread 1");
                alphonse.bow(gaston);
               // System.out.println("Th: gaston bowed to alphonse");
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
              //  System.out.println("Thread 2");
                gaston.bow(alphonse);
              //  System.out.println("2.gaston waiting alph bowed");
            }
        }).start();
    }
}


输出为:

Alphonse: Gaston  has bowed to me!
Gaston: Alphonse  has bowed to me!


没有回头!

最佳答案

这里有两件重要的事情要理解:1)每个并发运行的线程在做什么? 2)涉及哪些锁?

首先,您创建了Friend类的两个实例:alphonse和gaston。这些对象均具有自己的锁。所以有两个锁:阿方斯锁和加斯顿锁。输入对象的同步方法时,它将锁定该对象的锁。当同步方法返回时,将释放锁定(解锁)。

现在的线程。您的第一个线程称为Alphone的线程(请注意,将大写字母A区分该线程与对象alphonse):(“ A:”表示这是Alphonse的线程。)

A: alphonse.bow(gaston) - acquires alphonse's lock
A: gaston.bowBack(alphonse) - acquires gaston's lock
A: both methods return, thus releasing both locks


同时,Gaston的线程执行此操作:(“ G:”表示这是Gaston的线程。)

G: gaston.bow(alphonse) - acquires gaston's lock
G: alphonse.bowBack(gaston) - acquires alphonse's lock
G: both methods return, thus releasing both locks


现在,我们可以将这两部分信息放在一起得出答案。线程可以以不同顺序交错(即,它们的事件发生)。例如,如果事件按以下顺序发生,则会发生死锁:

A: alphonse.bow(gaston) - acquires alphonse's lock
G: gaston.bow(alphonse) - acquires gaston's lock
G: attempts to call alphonse.bowBack(gaston), but blocks waiting on alphonse's lock
A: attempts to call gaston.bowBack(alphonse), but blocks waiting on gaston's lock to


在这种情况下,每个线程都被阻塞,等待获取另一个线程持有的锁。但是,这两个线程都不会释放它持有的锁,因为它必须完成其方法才能这样做,因为它被阻塞等待另一个线程,所以它无法执行。因此它们被卡在适当的位置-陷入僵局。

但是,另一种可能的交织(事件顺序)是其中一个线程在另一个线程真正开始执行之前完成的操作,如下所示:

A: alphonse.bow(gaston) - acquires alphonse's lock
A: gaston.bowBack(alphonse) - acquires gaston's lock
A: both methods return, thus releasing both locks
G: gaston.bow(alphonse) - acquires gaston's lock
G: alphonse.bowBack(gaston) - acquires alphonse's lock
G: both methods return, thus releasing both locks


在这种情况下,没有死锁。当您添加println且没有死锁时,最有可能发生的事情是额外的println的额外延迟会影响线程运行的调度和/或速率,以便您获得这种交错而不是死锁。

当结果取决于并发事件的顺序(即,它们的调度或它们运行的​​速率)时,这被称为“竞赛条件”。例如,在此程序中,哪个线程首先打印输出取决于它们的调度顺序或运行速率。线程是否死锁也取决于此。因此,通过干扰该速度(例如,通过添加额外的说明),您可能会影响比赛的结果-但这并不意味着比赛已经结束。

并非所有比赛条件都有可能导致死锁。但是,以我的经验,死锁只会在比赛条件下发生。 (也许某个认识的人可以评论这种情况是否必要,或者只是通常情况?)

死锁一开始并不容易理解。我已尽力解释,但是如果您对我的帖子有任何后续问题,请告诉我。 :)

07-27 19:57