2012-02-21 155 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來驗證。

相關問題