2015-06-19 156 views
2

我有一個關於管理線程的簡單問題。我有3個程序共享同一個信號量和一個許可證。在正常情況下,第一個過程需要此許可證,並在第二個過程中發佈兩個許可證。第二個流程版本3允許第三個流程。我舉了一個例子來說明我的問題。使用信號量造成死鎖

第一招:

public class Process0 extends Thread{ 

Semaphore s; 

public Process0(Semaphore s){ 
    this.s = s; 
} 

public void run(){ 
    try { 
     sleep(20000); 
     s.acquire(); 

     System.out.println("hello"); 
    } catch (InterruptedException ex) { 
     Logger.getLogger(Process.class.getName()).log(Level.SEVERE, null, ex); 
    } 

    s.release(2); 
    } 
} 

第二個過程:

public class Process1 extends Thread{ 

Semaphore s; 

public Process1(Semaphore s){ 
    this.s = s; 
} 

public void run(){ 
    try { 
     this.sleep(10000); 
     s.acquire(2); 
     System.out.println("Hello 2"); 
    } catch (InterruptedException ex) { 
     Logger.getLogger(Process1.class.getName()).log(Level.SEVERE, null, ex); 
    } 

    s.release(3); 
} 

}

還有最後一:

public class Process2 extends Thread{ 

    Semaphore s; 

    public Process2(Semaphore s){ 
     this.s = s; 
    } 

    public void run(){ 
     try { 

      System.out.println("Acquire process 3 "); 
      s.acquire(3); 

      System.out.println("Hello 3"); 
     } catch (InterruptedException ex) { 
      Logger.getLogger(Process2.class.getName()).log(Level.SEVERE, null, ex); 
     } 

     } 

    } 

的問題。當我運行這三個過程並確保過程3是第一個執行acquire。我會陷入僵局。進程2從不打印「Hello 3」,進程1從不打印「Hello 2」。爲什麼?

信號量s =新的信號量(1);

Process0 p = new Process0(s); 
Process1 p1 = new Process1(s); 
Process2 p2 = new Process2(s); 
p.start(); 
p1.start(); 
p2.start(); 
+0

您如何確保流程3首先獲得它?首先啓動並不意味着它會先執行。對於這個問題,睡覺也沒有。 – zubergu

+0

我在獲取Process和Process1 – Mehdi

+1

之前添加睡眠,可能會或可能無法完成您想要的操作。根據線程執行順序來設計應用程序從一開始就是錯誤的。 – zubergu

回答

3

您的信號燈被修建爲new Semaphore(1),其中只有一個可以獲得的許可證。電話s.acquire(3)將永遠不會返回,因爲信號燈永遠不會有三個可用的許可證。通過Process也塊採集單個許可證的嘗試,因爲收購的有序和Process2到了「第一」:

release方法的javadoc狀態,當

其他某些線程調用釋放可能發生的收購( )方法爲此信號量和當前線程旁邊將被分配許可

這個最小的,單線程的例子會告訴你:

Semaphore s = new Semaphore(1); 
s.acquire(2); 
System.out.println("Didn't deadlock!"); 

對此的解決方案是使用Semaphore.acquire()哪些請求一個許可證,或Semaphore.acquire(1)這也要求只有一個許可證。

您還需要確保您獲得並釋放相同數量的許可證,除非您有非常誤用Semaphore的好理由。來自Javadoc:

沒有要求釋放許可證的線程必須通過調用acquire獲取許可證[或者線程釋放所有許可證]。正確使用信號量是通過編程約定在應用程序中建立的。

此外,似乎您可能會在此任務中使用錯誤的同步器。您可以使用CyclicBarrier或其他class usable for synchronization

+0

Process2從不打印「你好3」 – Mehdi

+0

@Mehdi更新。 – hexafraction

+0

當process1完成執行時,我發佈了release(3)。但Process1永遠不會獲得!所以,它永遠不會釋放... – Mehdi

0

我花了一些時間找出你想完成的事情。這是我想出的。希望能幫助到你。

問題是,您的實現中的線程正嘗試以某種順序獲取鎖。所以等待3個許可證的線程先等待,然後等待2個許可證的線程,顯然排隊等待他的2個許可證,然後是第一個只需要1個許可證的線程。有一個許可證可用,所以它很好。然後它返回2個許可證。不幸的是,下一個線程正在等待3個許可證的線程,而不是等待2. Bummer。阻止。這就是你觀察到的。

如果您讓其他線程更改排名獲取地點,一切都會好起來的。這裏來

s.tryAcquire(int permits) 

和突然一切工作正常。

我會根據你的代碼做一個例子,在忙碌的等待循環中睡1秒,看看發生了什麼。

import java.util.concurrent.Semaphore; 

class Process0 extends Thread { 

    Semaphore s; 

    public Process0(Semaphore s){ 
     this.s = s; 
    } 

    public void run(){ 
     try { 
      sleep(20000); 
      s.acquire(); 

      System.out.println("hello"); 
     } catch (InterruptedException ex) { 
      System.out.println(Process.class.getName()); 
     } 

     s.release(2); 
     System.out.println("released 2"); 
     } 
    } 

class Process1 extends Thread{ 

    Semaphore s; 

    public Process1(Semaphore s){ 
     this.s = s; 
    } 

    public void run(){ 
     try { 
      this.sleep(10000); 
      while(!s.tryAcquire(2)) { 
       System.out.println("Busy waiting for 2 permits"); 
       sleep(1000); 
      } 
      System.out.println("Hello 2"); 
     } catch (InterruptedException ex) { 
      System.out.println(Process.class.getName()); 
     } 

     s.release(3); 
     System.out.println("Released 3"); 
    } 
} 


class Process2 extends Thread{ 

     Semaphore s; 

     public Process2(Semaphore s){ 
      this.s = s; 
     } 

     public void run() { 
      System.out.println("Acquire process 3 "); 
      while(!s.tryAcquire(3)) { 
       System.out.println("Busy waiting for 3 permits"); 
       try { 
        sleep(1000); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
      } 

      System.out.println("Hello 3"); 

      } 

     } 



public class DaemonTest { 
    public static void main(String[] args) { 
     Semaphore s = new Semaphore(1); 

     Process0 p = new Process0(s); 
     Process1 p1 = new Process1(s); 
     Process2 p2 = new Process2(s); 
     p.start(); 
     p1.start(); 
     p2.start(); 
    } 
}