当前位置: 首页 > 知识库问答 >
问题:

死锁使用信号量

诸葛令
2023-03-14

我有一个关于管理线程的简单问题。我有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是第一个执行获取的进程时。我会死锁。进程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();

共有2个答案

孙福
2023-03-14

我花了一些时间才知道你想要完成什么。这是我想到的。希望有帮助。

问题是您的实现中的线程试图以某种顺序获取锁。所以等待3个许可的线程首先等待,然后是等待2个许可的线程,显然排队等待他的2个许可,然后是第一个只想要1个许可的线程。有一个可用的许可,所以可以走了。然后它返回2个许可。不幸的是,下一个排队的是等待3个许可的线程,而不是等待2。糟糕。阻塞。这就是你观察到的。

若您让其他线程在acquire的队列中更改位置,那个么一切都会很好。来了

s.tryAcquire(int permits)

突然间,一切都很好。

我将根据您的代码进行示例,在忙等待循环中使用1s睡眠来查看发生了什么。

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();
    }
}
鲁建茗
2023-03-14

您的信号量构造为new Semaphore(1),它只有一个可用的许可证可供获取。调用s.acquire(3)将永远不会返回,因为信号量永远不会有三个可用的许可证。通过Process获取单个许可证的尝试也会被阻止,因为收购是有序的并且Process2“首先”到达:

发布方法javadoc指出,当

其他一些线程为此信号量调用release()方法,当前线程下一个将被分配一个许可证。

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

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

解决方法是使用信号量。acquire()请求一个许可证,或信号量。获取(1),它也只需要一个许可证。

您还需要确保获得和发布相同数量的许可,除非您有很好的理由滥用信号量。来自Javadoc:

不要求释放许可证的线程必须通过调用acquire获得该许可证[或者线程释放其所有许可证]。信号量的正确使用是通过应用程序中的编程约定建立的。

此外,您可能为此任务使用了错误的同步器。您可以使用CyclicBarrier或其他可用于同步的类。

 类似资料:
  • 在使用信号量时,我应该注意多线程问题吗?在我的测试之后,似乎有一段时间信号灯#release not cause acquire wake up,即使有足够的许可证。 底部是我的测试代码。 带有2个许可证的信号灯 Thread3和Thread2先向上 线程3获取许可,等待锁,锁将由线程1通知 线程2获取许可,等待锁1,锁1将由线程3通知 线程1启动,线程1和线程2先睡眠30ms启动 线程1通知锁定

  • 问题内容: 我已升级到最新的Java 7 build 1.7.0_60-b19,但问题仍然存在。 我进行了另一个线程转储,发现了相同的问题:在DefaultCorrelatingMessageHandler锁调用中,所有DefaultMessageListenerContainers(计数为20)都由taskScheduler(entityScheduler-3)锁定。 这是调度程序和聚合器配置:

  • 死锁概念 死锁(Deadlock)就是一个进程拿着资源A请求资源B,另一个进程拿着资源B请求资源A,双方都不释放自己的资源,导致两个进程都进行不下去。 示例程序 我们可以写代码模拟进程死锁的例子。 package main func main() { ch := make(chan int) <-ch } 运行结果 root@fa13d0439d7a:/go/src# go run de

  • 问题内容: 信号量和自旋锁之间的基本区别是什么? 什么时候在自旋锁上使用信号灯? 问题答案: 自旋锁和信号灯的主要区别在于四点: 1.他们是什么 一个 自旋锁 是一个可能实现的锁,即一个由忙等待(“旋转”)来实现。信号量是锁的概括(或者相反,锁是信号量的特例)。通常( 但不是必须) ,自旋锁仅在一个进程内有效,而信号量也可用于在不同进程之间进行同步。 锁用于互斥,即 一次一个 线程可以获取该锁并继

  • 问题内容: 我有一个Java Servlet应用程序,并且正在使用准备好的查询来更新SQL Server数据库表中的记录。 可以说我要执行。(是的,id是一个varchar) 我使用以下代码来实现此目的: 我发现运行JMeter脚本模拟2个用户时,此语句在数据库中导致死锁。 我想检查一下SQL事件探查器中的值,因此我使用了以下代码,因此可以检查这些值。 突然我的僵局消失了!遗憾的是,最后一种方法容

  • 本文向大家介绍互斥锁死锁,包括了互斥锁死锁的使用技巧和注意事项,需要的朋友参考一下 死锁可以在使用互斥锁的多线程Pthread程序中发生。让我们看看它如何发生。未锁定的互斥锁由pthread_mutex_init()函数初始化。 使用pthread_mutex_lock()和pthread_mutex_unlock()获取并释放互斥锁。如果线程尝试获取锁定的互斥锁,则对pthread_mutex_