2015-06-19 200 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(); 
    } 
}