You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Semaphore方法异常:生产者消费者模型死锁问题求助

生产者消费者模型中Semaphore引发的死锁问题排查

问题描述

刚接触生产者消费者问题和Semaphore,代码出现死锁:生产者向队列添加元素后,再次获取许可时陷入阻塞,此时消费者线程未运行。代码如下:

public class UsingSemaphore {

    volatile static boolean check = true;

    public static void main(String args[]) throws InterruptedException {

        Semaphore semCon = new Semaphore(0);

        Semaphore semProd = new Semaphore(1);

        Queue<Integer> q = new LinkedList<>();


        // Producer lambda
        Runnable producer = () -> {
            while (check) {
                try {
                    semProd.acquire();

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    
                    Random rand = new Random();
                    q.add(rand.nextInt(10));    
                }finally {
                    semCon.release();

                }
            }   
        };
                //Consumer lambda
        Runnable consumer = () -> {
            while (check) {
                try {

                    semCon.acquire();

                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                try {
                    System.out.println("Consumer item " + q.remove());
                
                }finally {
                    semProd.release();

                }
            }
        };  
        
        ThreadGroup pg = new ThreadGroup("PG");
        ThreadGroup cg = new ThreadGroup("CG");

        Thread p1 = new Thread(pg, producer, "p1");

        Thread c1 = new Thread(cg, consumer, "c1");


        p1.start();

        c1.start();

        Thread.sleep(10);
        check = false;
    }
}

死锁原因分析

死锁的核心是线程终止时机不合理,导致信号量许可未被正确释放:

  1. 生产者线程启动后,首先获取semProd许可(许可数从1→0),生产元素后释放semCon(许可数从0→1)。
  2. 生产者再次进入循环(此时check仍为true),尝试再次获取semProd许可,但此时许可数为0,进入阻塞状态。
  3. 主线程仅等待10ms就将check设为false,消费者线程刚启动进入循环时,发现check为false,直接退出循环,从未执行semCon.acquire()和后续的semProd.release()操作。
  4. 生产者线程永远阻塞在semProd.acquire()调用上,没有线程会再释放semProd许可,最终形成死锁。

另外还有一个潜在问题:LinkedList并非线程安全队列,多线程直接操作会引发并发修改异常或数据不一致问题。

修复方案

1. 调整线程终止逻辑,确保信号量正确释放

将check的判断移至信号量获取之后,保证线程即使收到终止信号,也能完成当前的生产/消费流程,释放对应的信号量:

public class UsingSemaphore {

    volatile static boolean check = true;

    public static void main(String args[]) throws InterruptedException {

        Semaphore semCon = new Semaphore(0);
        Semaphore semProd = new Semaphore(1);
        // 使用线程安全的ConcurrentLinkedQueue替代LinkedList
        Queue<Integer> q = new ConcurrentLinkedQueue<>();

        // Producer lambda
        Runnable producer = () -> {
            while (true) {
                try {
                    semProd.acquire();
                    // 获取许可后再检查终止信号
                    if (!check) {
                        semProd.release(); // 释放许可,避免后续阻塞
                        break;
                    }
                    Random rand = new Random();
                    q.add(rand.nextInt(10));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                    break;
                } finally {
                    semCon.release();
                }
            }
        };

        // Consumer lambda
        Runnable consumer = () -> {
            while (true) {
                try {
                    semCon.acquire();
                    // 获取许可后再检查终止信号
                    if (!check) {
                        semCon.release(); // 释放许可,避免后续阻塞
                        break;
                    }
                    System.out.println("Consumer item " + q.remove());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                    break;
                } finally {
                    semProd.release();
                }
            }
        };

        ThreadGroup pg = new ThreadGroup("PG");
        ThreadGroup cg = new ThreadGroup("CG");

        Thread p1 = new Thread(pg, producer, "p1");
        Thread c1 = new Thread(cg, consumer, "c1");

        p1.start();
        c1.start();

        // 适当延长等待时间,让线程完成至少一轮生产消费
        Thread.sleep(100);
        check = false;
        // 等待线程终止
        p1.join();
        c1.join();
    }
}

2. 关键修改点说明

  • 将while(check)改为while(true),在获取信号量后再判断终止条件,确保每次获取许可后都能完成对应操作并释放信号量。
  • 使用ConcurrentLinkedQueue替代LinkedList,保证队列操作的线程安全性。
  • 主线程调用join()等待生产者和消费者线程完全终止,避免程序提前退出。

额外提示

  • Semaphore的核心是维护许可计数,必须严格保证acquire()和release()的配对调用,否则会导致许可泄漏或死锁。
  • 多线程环境下,所有共享资源(如队列)必须保证线程安全,避免并发问题。

内容的提问来源于stack exchange,提问作者Chetan Sharma 417

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 08:24:52