2012-02-21 105 views
9
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(); 
    } 
} 
当我运行这个程序,我得到的输出作为

两个线程可以同时访问同步方法吗?

阿尔

:加斯顿已同意在我!加斯东:阿尔方斯向我鞠躬!

那么两个线程可以同时访问同步方法吗?

+0

可能重复的[尝试包裹我的脑围绕线程死锁如何](http://stackoverflow.com/questions/749641/trying-to-wrap-my-wee-brain-around-how-threads-deadlock ) – 2014-07-09 12:34:10

回答

7

可以两个线程同时访问一个同步的方法?

实例方法(如您的示例)在包含它们的对象上同步。在这种情况下,当您拨打alphonse.bow(...)时,您锁定了alphonse对象。 gaston.bow(...)gaston

有几个方法,你可以得到一个对象的多个实例锁定同一对象上。

  • 你可以做的方法是static和​​在这种情况下,他们将锁定类对象本身。每个类加载器只有一个这样的对象。

    public static synchronized void bow(Friend bower) { 
    
  • 它们都可以锁定定义的静态对象。喜欢的东西:

    private static final Object lockObject = new Object(); 
    ... 
    public void bow(Friend bower) { 
        synchronized (lockObject) { 
         .... 
        } 
    } 
    
  • 或者你可以可以在对象传递给锁定,如果你不想让静态的。

你的输出可能会是这样的:

  1. gaston线程(可能)开始第一和调用bow(alphonse)
  2. 这个锁定gaston对象和输出:Gaston: Alphonse has bowed to me!
  3. 它调用alphonse.bowBack(this)
  4. 此呼叫锁定alphonse对象和输出:Alphonse: Gaston has bowed back to me!
  5. alphonse.bowBack(this)退出时,解锁alphonse对象。
  6. gaston.bow(alphonse)退出,解锁gaston对象。
  7. 然后gaston线程退出。
  8. alphonse螺纹(可以)开始下一个,并调用bow(gaston)
  9. 此锁定alphonse对象和输出:Alphonse: Gaston has bowed to me!
  10. 它调用gaston.bowBack(this)
  11. 此呼叫锁定gaston对象和输出:Gaston: Alphonse has bowed back to me!
  12. gaston.bowBack(this)退出时,解锁gaston对象。
  13. alphonse.bow(gaston)退出,解锁alphonse对象。

这可能发生在许多不同的顺序。即使start()方法稍后被调用,alphonse线程也可以先运行。如果alphonse.bowBack(...)当前正在运行,那么锁只能拨打alphonse.bow(...)。正如@ user988052指出的那样,由于每个线程都锁定了自己的对象,然后试图锁定另一个对象,所以很容易发生死锁。

+0

ok..but有时当我运行这个程序时,我得到的输出为: Gaston:Alphonse向我鞠躬! Alphonse:Gaston已经向我鞠躬! Alphonse:Gaston向我鞠躬!加斯东:阿尔方斯向我鞠躬! 因此,如果发生这种情况..我的意思是这个程序应该像在教程中一样被阻止.. @ – harish 2012-02-21 17:47:05

+0

@harish:它发生是因为第一个线程在第二个线程开始之前可能已经完成了它的工作。这就是为什么要创建一个确定的(至少在一个非常可能的情况下)一个死锁,你必须实例化很多线程(请参阅我的答案)。 – TacticalCoder 2012-02-21 17:54:27

+0

@ user988052你知道了..谢谢你.. – harish 2012-02-21 18:02:12

10

这样可以两个线程同时访问一个同步的方法?

是的,没有:

  • 是,如果该方法被调用的类的不同实例。

  • 没有,两个线程不能同时调用同步方法上类的相同实例。即使两个线程调用不同的方法(只要实例相同),情况也是如此。

+2

注意:如果一个同步方法在同一个对象上调用另一个同步方法,那么一个线程可以同时在同一个对象上的两个同步方法中。 – 2012-02-21 17:27:02

+0

@aix - 你能否修改你的答案来表明同步正在课上完成。此外,您的第一个要点对于类同步不正确。 – Perception 2012-02-21 17:35:35

+0

@Perception:不确定我关注。由于这些方法不是'static',所以同步会锁定实例,而不是类。你能解释一下你的意思吗? – NPE 2012-02-21 17:37:13

0

使用synchronized关键字可以锁定实例方法的实例或静态方法的类。因此,在这里,您保证最多一个线程在给定时间执行弓或bowback(如果一个线程执行弓,没有其他线程可以执行bowback,因为两种方法在同一个锁上同步)...

One更多评论:因为锁一旦线程获得了锁就可以重入,它可以输入其他方法在同一个锁上同步。

1

Deadlock Tutorials所述,这是代码来自的地方,这段代码通常会被阻塞。

当Deadlock运行时,当它们尝试调用bowBack时,这两个线程很可能会被阻塞。这两个块都不会结束,因为每个线程都在等待另一个线程退出低头。

2

我没有详细检查你的代码,但我想我认识典型的例子为如何建立一个僵局。

但是,您不应该只调用一次来尝试创建死锁。

在一个循环中创建线程,有一个非常高的概率,你会得到你的僵局:

for (int i = 0; i < 1000; i++) { 
    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(); 
} 

请注意,你不会死锁住2000多个线程:只有一些人会陷入僵局。你可以通过获取你的程序/ JVM的threadump来验证。