使用信号量的死锁

时间:2015-06-19 10:00:07

标签: java multithreading deadlock semaphore

我有一个关于管理线程的简单问题。我有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”。为什么?

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();

2 个答案:

答案 0 :(得分:3)

您的信号量构建为new Semaphore(1),只有一个许可证可供获取。由于信号量永远不会有三个许可证,因此呼叫s.acquire(3)将永远不会返回。 Process获取单一许可的尝试也会阻止,因为已经订购了收购并且Process2已到达"首先":

release方法javadoc声明可以在

时进行采集
  

其他一些线程调用此信号量的release()方法,然后为当前线程分配许可

这个最小的单线程示例将向您显示:

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

答案 1 :(得分: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();
    }
}