小编典典

System.out.format如何防止死锁?

java

我发现经典的Java Deadlock
Tutorial
中包含对System.out.format的调用将防止死锁的发生,我不知道为什么。

下面的代码是相同的教程,与除mainSystem.out.format("Hi, I'm %s...no deadlock for you!\n\n", alphonse.getName());

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) throws InterruptedException {
        final Friend alphonse = new Friend("Alphonse");
        final Friend gaston = new Friend("Gaston");

        System.out.format("Hi, I'm %s...no deadlock for you!\n\n", alphonse.getName());

        new Thread(new Runnable() {
            public void run() { alphonse.bow(gaston); }
        }).start();

        new Thread(new Runnable() {
            public void run() { gaston.bow(alphonse); }
        }).start();
    }
}

这是输出:

Hi, I'm Alphonse...no deadlock for you!

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

删除违规行会导致通常的死锁:

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

对System.out.format的调用是否以某种方式改变了线程获取对象内在锁的方式?

更新:

通过更改代码中启动线程的位置,我能够使系统再次陷入僵局:

public static void main(String[] args) throws InterruptedException {
    final Friend alphonse = new Friend("Alphonse");
    final Friend gaston = new Friend("Gaston");

    System.out.format("Hi, I'm %s...no deadlock for you!\n\n", alphonse.getName());

    Thread t1 = new Thread(new Runnable() {
        public void run() { alphonse.bow(gaston); }
    });

    Thread t2 = new Thread(new Runnable() {
        public void run() { gaston.bow(alphonse); }
    });

    t1.start();
    t2.start();
}

这就引出了一个问题,那就是我们如何才能更深入地了解线程调度程序的行为,但是我将其另存为另一天。


阅读 198

收藏
2020-11-30

共1个答案

小编典典

您并未真正消除 死锁 ,而是(由于某些内部JVM原因)更改了线程的计时,以使其中一个线程bowBack() 其他调用 之前
进入bow()。只需输入bowsleep(1000),您的僵局就会再次出现。

请注意,只有在线程处于 幸运 时机时,死锁并不 总是 发生。在这种情况下,当两个线程都进入并且它们中的任何一个都调用之前,就会发生死锁
__bow``bowBack

…并且 “某些内部JVM原因” 可以如下:

在您的情况下,实际上有三个线程:一个执行 maint1t2 。放置 打印
隐藏死锁的原因可能是线程调度程序决定main仍有工作要做,即刷新io缓冲区,因此让 main 在开始 t1之后
和开始t2之前继续。如果你是一个双核CPU,只maint1会运行,但t2会等待,因为print是一个缓慢的操作。上下文切换将花费更多时间,并且
t1 将在 t2 之前完成 __可以开始…所以不会发生死锁。 但这并不意味着如果再次运行该程序,就不会发生死锁。

If you want to play, create a queue and push tokens (thread names) in that
queue, then join your threads in main. After they finish, print the queue
content and you can observe the timing of the threads.

2020-11-30