2012-09-16 67 views
1

我发现在经典Java Deadlock Tutorial中包含对System.out.format的调用将防止发生死锁,并且我找不到原因。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的调用。格式以某种方式改变线程获取对象的内部锁的方式?

更新:

我能得到系统只是通过改变再次陷入僵局,我的代码启动线程:

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(); 
} 

这就引出了我们如何能够得到更深入的了解这个问题进入线程调度程序的行为方式,但我会在不同的一天保存它。

+0

当您添加该行时,第一个线程在主线程启动第二个线程之前完成。 –

+0

但是,这只是巧合在一个特定的处理器和调度程序。仍然不能保证不会发生死锁。 – Wyzard

回答

5

你并没有真正删除僵局而是(因为内部的一些JVM原因)改变,其他电话bow()线程的时间,使一个线程进入bowBack()之前。 只要在bowsleep(1000)和您的死锁将重新出现。

请注意,死锁不会总是发生,只有当线程在幸运时间。在这种情况下,当两个线程进入bow僵局会发生,并呼吁bowBack

... 而“一些内部JVM原因”之前任何一个可以是以下:

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

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

0

format()并且写入控制台通常是费用较高的操作。我猜想它的执行正在改变线程启动的时间,因此第二个线程开始的时间太晚,以至于不会影响第一个线程。