使用信号量的死锁

deadlock using 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"。为什么?

信号量 s = 新信号量(1);

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

您的 Semaphore 构造为 new Semaphore(1),它只能获得一个许可。调用 s.acquire(3) 永远不会 return 因为信号量永远不会有三个可用的许可。 Process 尝试获取单个许可证的尝试也被阻止,因为已订购并且 Process2 已到达 "first":

release 方法 javadoc 声明在

时可以发生获取

Some other thread invokes the release() method for this semaphore and the current thread is next to be assigned a permit.

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

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

解决方法是使用 Semaphore.acquire() 请求一个许可,或者 Semaphore.acquire(1) 也只请求一个许可。

您还需要确保获得和释放相同数量的许可,除非您有非常 滥用 Semaphore 的充分理由。来自 Javadoc:

There is no requirement that a thread that releases a permit must have acquired that permit by calling acquire [or that a thread releases all of its permits]. Correct usage of a semaphore is established by programming convention in the application.

此外,您似乎为此任务使用了错误的同步器。您可以使用 CyclicBarrier 或其他 class usable for synchronization.

我花了一些时间才弄清楚你想要完成什么。这是我想出的。希望对你有帮助。

问题是您的实现中的线程试图按某种顺序获取锁。所以等待 3 个许可的线程首先等待,然后是等待 2 个许可的线程,显然排队等待他的 2 个许可,然后是第一个只想要 1 个许可的线程。有一个可用的许可证,所以很高兴。然后它 returns 2 许可。不幸的是,接下来是等待 3 个许可的线程,而不是等待 2 个许可的线程。真可惜。阻止。这就是你观察到的。

如果你让其他线程改变队列获取的位置,一切都会好起来的。来了

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