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

我发现在经典Java死锁教程中包含对System.out.format的调用可以防止发生死锁,我无法弄清楚原因。

下面的代码与教程的代码相同,添加了System.out.format("Hi, I'm %s...no deadlock for you!\n\n", alphonse.getName()); main内容System.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(); } 

这引出了一个问题,即我们如何能够更深入地了解线程调度程序的行为,但我将在不同的日子保存它。

你并没有真正消除死锁 ,而是(因为一些内部JVM的原因)改变了线程的时间,以便其中一个线程在另一个线程进入bowBack() 之前进入bowBack() bow() 。 只要投入bowsleep(1000) ,你的僵局就会重新出现。

请注意,只有当线程处于幸运时间时,才会发生死锁。 在这种情况下,当两个线程进入bow并且在它们中的任何一个调用bowBack之前,将发生死锁

……而“一些内部JVM原因”可能如下:

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

如果你想玩,创建一个queue并在该队列中推送令牌(线程名称),然后在main中join你的线程。 完成后,打印队列内容,您可以观察线程的时间。

format()和写入控制台通常是费用操作。 我猜它的执行正在改变线程启动的时间,所以第二个线程启动得太迟,以至于它不会干扰第一个线程。