2013-03-15 232 views
1

爲什麼下面這段代碼不會導致死鎖?爲什麼這不會導致死鎖

從我有限的多線程編程的理解,當getBar1()叫,sharedBuffer將被「鎖定」,因此,當方法試圖調用getBar2(),該線程將不得不等待sharedBuffer(由其自身持有!)。換句話說,getBar2()不能返回,直到getBar1()有(並且發佈sharedBuffer)。但另一方面,getBar1()無法返回,因爲它正在等待getBar2()返回。

==>死鎖。 (但實際上,它不是,這就是爲什麼我很困惑)

... 
Foo sharedBuffer = new Foo(); 

Bar1 getBar1() 
{ 
    Bar1 bar1; 
    synchronized (sharedBuffer) 
    { 
      bar1 = sharedBuffer.getBar1(); 
      if (bar1 == null) 
       bar1 = new Bar1(sharedBuffer, getBat2()); 
      sharedBuffer.setBar1(bar1); 
    } 
    return bar1; 
} 

Bar2 getBar2() 
{ 
    Bar2 bar2; 
    synchronized (sharedBuffer) 
    { 
     bar2 = sharedBuffer.getBar2(); 
     if (bar2 == null) 
      bar2 = new Bar2(); 
    } 
    return bar2; 
} 
... 
+0

閱讀有關重入鎖的信息。那些在Java中是本地的是。 – 2013-03-15 16:44:33

回答

3

Java的顯示器是遞歸的,這意味着同一個線程可以獲取同樣的鎖幾次。

從JLS(§17.1 Synchronization):

線程t可以鎖定特定的監視器多次;每次解鎖都會反轉一次鎖定操作的效果。

+0

啊,所以'getBar1()'訪問'sharedBuffer'並不會阻止它調用'getBar2()',它也會訪問'sharedBuffer'? – 2013-03-15 16:46:13

+0

@OneTwoThree:的確如此。 – NPE 2013-03-15 16:48:24

+0

但是,如果其他線程試圖調用'getBar2()',它會阻塞,直到這個完成? – 2013-03-15 16:52:00

0

當併發操作嘗試以不同順序鎖定兩個或多個資源時,會發生死鎖,並且它們都等待資源被另一個鎖定。

  • T1同步於R1:

    例如,線程T1和T2上的資源R1和R2同步。

  • 調度程序決定T2應運行
  • T2在R2上同步。
  • T2嘗試在R1上同步;它被迫等到T1放棄鎖定。
  • 調度器看到該T2不能繼續運行,所以允許T1運行
  • T1試圖在同步R2;它被迫等到T2放棄鎖定。
  • 兩個線程可以繼續

你在做什麼這裏是基本同步的,只允許一個對象在同一時間訪問sharedBuffer

0

它沒有死鎖,因爲你真的只有一個鎖。在這兩個函數中,您都鎖定在sharedBuffer上。當第一個線程調用getBar1()時,它鎖定在sharedBuffer上。當同一個線程調用getBar2()時,它會碰到synchronized塊並且已經擁有鎖,所以它只是進入鎖。

如果您想引起死鎖,請使用兩個不同的值來鎖定。然後,如果時間順序正確,你只能看到它。如果你想強制一個死鎖,確保第一個線程休眠足夠長的時間,以便第二個線程獲得一個鎖。

這裏有一些代碼會死鎖...(未經測試,prolly有錯別字)。這應該起作用,因爲不同的線程擁有的鎖比想要鎖的鎖要好。

public class Deadlock extends Thread 
{ 
    private Deadlock other; 
    private String name; 

    public static void main(String[] args) 
    { 
     Deadlock one = new Deadlock("one"); 
     Deadlock two = new Deadlock("two"); 
     one.setOther(two); 
     two.setOther(one); 
     one.start(); 
     two.start(); 
    } 

    public setOther(Deadlock other){ this.other = other; } 

    public void run() { 
     deadlock(); 
    } 

    public synchronized deadlock() { 
     System.out.println("thread " + this.name + " entering this.deadlock()"); 
     sleep(1000); // adjust as needed to guarantee deadlock 
     System.out.println("thread " + this.name + " calling other.deadlock()"); 
     other.deadlock(this.name); 
     System.out.println(name + " - deadlock avoided!"); 
    } 
}