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

Java生产者消费者问题:线程调用notify后仍无限等待求助

Java生产者消费者模型线程无限等待问题排查

我自行实现了Java生产者消费者模型,但线程调用notify()后仍陷入无限等待状态,无法确定问题原因。以下是我的代码实现、程序输出及线程Dump信息,请求排查问题根源。

代码实现

MainClass.java

public class MainClass {
    public static void main(String[] args) throws InterruptedException {
        final Thread produce = new Thread(new Runnable() {
            @Override
            public void run() {
                    synchronized (Shop.LL) {
                        if (null != Shop.LL && Shop.LL.size() == 0) {
                            try {
                                Producer.produce();
                                Shop.LL.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            notify();
                    }}}});

        final Thread consume = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (Shop.LL) {
                    System.out.println("Entering consumer");
                    try {
                        Consumer.buy();
                        Shop.LL.wait();
                    } catch (InterruptedException e) {e.printStackTrace();}
                notify();

                }}});

        produce.start();
        consume.start();
        produce.join();
        consume.join();
    }}

Producer.java

public class Producer {
    private static String[] fruits = { "apple", "orange", "pineapple", "banana", "cherry", "kiwi" };
    protected static synchronized void produce() {
        if (Shop.LL.size() == 0) {
            System.out.println("Producer selling the .....");
            do {
                System.out.println(fruits[Shop.i]);
                Shop.LL.add(fruits[Shop.i]);
                Shop.i++;
            } while (Shop.LL.size() <= 1);
        }}}

Consumer.java

public class Consumer {
        protected static synchronized void buy() {
            int i = 0;
            if (Shop.LL.size() > 0) {
                System.out.println("Cosumer buying the .....");
                while (i < 2) {
                    System.out.println(Shop.LL.get(i));
                    i++;
                }}
            Shop.LL.clear();
        }}

Shop.java

import java.util.LinkedList;
import java.util.List;
public class Shop {
    protected static List<String> LL = new LinkedList<String>();
    protected static int i = 0;
}

程序输出

Entering producer 
Producer selling the ..... apple orange 
Entering consumer 
Cosumer buying the ..... apple orange

执行到此处后,两个线程均陷入无限等待状态。

线程Dump信息

8976:
2023-03-24 13:43:31
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.191-b12 mixed mode):

"Thread-1" #11 prio=5 os_prio=0 tid=0x000000001869b000 nid=0x2ca8 in Object.wait() [0x0000000018fae000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000d5f4ee28> (a java.util.LinkedList)
    at java.lang.Object.wait(Unknown Source)
    at com.consumer.producer.MainClass$2.run(MainClass.java:58)
    - locked <0x00000000d5f4ee28> (a java.util.LinkedList)
    at java.lang.Thread.run(Unknown Source)

"Thread-0" #10 prio=5 os_prio=0 tid=0x0000000018694000 nid=0x1328 in Object.wait() [0x0000000018eaf000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000d5f4ee28> (a java.util.LinkedList)
    at java.lang.Object.wait(Unknown Source)
    at com.consumer.producer.MainClass$1.run(MainClass.java:25)
    - locked <0x00000000d5f4ee28> (a java.util.LinkedList)
    at java.lang.Thread.run(Unknown Source)

"Service Thread" #9 daemon prio=9 os_prio=0 tid=0x00000000185b8000 nid=0x7f0 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread2" #8 daemon prio=9 os_prio=2 tid=0x00000000185b2800 nid=0x11f8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #7 daemon prio=9 os_prio=2 tid=0x0000000016eac000 nid=0x21c8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #6 daemon prio=9 os_prio=2 tid=0x0000000016e4d800 nid=0x30d0 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000000016e4b800 nid=0x2de0 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000000016e4a000 nid=0xe38 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000000230b800 nid=0x2688 in Object.wait() [0x00000000181ae000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000d5e08ed0> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(Unknown Source)
    - locked <0x00000000d5e08ed0> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(Unknown Source)
    at java.lang.ref.Finalizer$FinalizerThread.run(Unknown Source)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000016e19000 nid=0x2b4c in Object.wait() [0x00000000180ae000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000d5e06bf8> (a java.lang.ref.Reference$Lock)
    at java.lang.Object.wait(Unknown Source)
    at java.lang.ref.Reference.tryHandlePending(Unknown Source)
    - locked <0x00000000d5e06bf8> (a java.lang.ref.Reference$Lock)
    at java.lang.ref.Reference$ReferenceHandler.run(Unknown Source)

"main" #1 prio=5 os_prio=0 tid=0x00000000020be800 nid=0x1908 in Object.wait() [0x000000000220f000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000000d5e869f0> (a java.lang.Thread)
    at java.lang.Thread.join(Unknown Source)
    - locked <0x00000000d5e869f0> (a java.lang.Thread)
    at java.lang.Thread.join(Unknown Source)
    at com.consumer.producer.MainClass.main(MainClass.java:79)

"VM Thread" os_prio=2 tid=0x0000000016e17800 nid=0x3af8 runnable 

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002228000 nid=0x2e60 runnable 

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002229800 nid=0x16c4 runnable 

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000000000222b000 nid=0xe9c runnable 

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000000000222d800 nid=0x22e4 runnable 

"VM Periodic Task Thread" os_prio=2 tid=0x00000000185ba000 nid=0x21f0 waiting on condition 

JNI global references: 4

问题根源分析

  1. notify()调用对象错误:生产者和消费者线程中直接调用notify(),等价于this.notify(),但线程持有的是Shop.LL的锁,必须调用Shop.LL.notify()才能唤醒等待该对象的线程,当前调用无法起到唤醒作用。
  2. 线程执行逻辑缺陷:生产者生产后直接进入等待,消费者消费后也进入等待,两个线程都陷入等待状态,且没有其他线程唤醒它们;同时生产者的if判断只会执行一次,无法循环生产,消费者也无循环消费逻辑,不符合模型的循环执行要求。
  3. 不必要的同步锁:Producer.produce()和Consumer.buy()使用静态同步方法,会锁住类对象,与线程持有的Shop.LL锁造成额外竞争,且完全多余。

修复方案

修改后的MainClass.java

public class MainClass {
    public static void main(String[] args) throws InterruptedException {
        final Thread produce = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized (Shop.LL) {
                        // 队列满时等待(设定最大容量为2)
                        while (Shop.LL.size() >= 2) {
                            try {
                                Shop.LL.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        Producer.produce();
                        // 唤醒等待Shop.LL的线程
                        Shop.LL.notify();
                    }
                }
            }
        });

        final Thread consume = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized (Shop.LL) {
                        // 队列空时等待
                        while (Shop.LL.size() == 0) {
                            try {
                                Shop.LL.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("Entering consumer");
                        Consumer.buy();
                        // 唤醒等待Shop.LL的线程
                        Shop.LL.notify();
                    }
                }
            }
        });

        produce.start();
        consume.start();
        produce.join();
        consume.join();
    }
}

修改后的Producer.java

public class Producer {
    private static String[] fruits = { "apple", "orange", "pineapple", "banana", "cherry", "kiwi" };
    protected static void produce() {
        System.out.println("Producer selling the .....");
        // 每次生产一个产品
        System.out.println(fruits[Shop.i]);
        Shop.LL.add(fruits[Shop.i]);
        Shop.i = (Shop.i + 1) % fruits.length; // 循环使用水果数组
    }
}

修改后的Consumer.java

public class Consumer {
    protected static void buy() {
        System.out.println("Consumer buying the .....");
        // 每次消费一个产品
        System.out.println(Shop.LL.remove(0));
    }
}

修复说明

  • 将notify()改为Shop.LL.notify(),确保唤醒等待目标对象的线程。
  • 使用while循环替代if判断,处理线程虚假唤醒问题,同时实现循环生产/消费逻辑。
  • 移除produce()和buy()的synchronized修饰符,因为线程已在Shop.LL的同步块中,无需额外同步。
  • 调整生产消费逻辑为每次处理一个产品,符合典型生产者消费者模型的行为。

内容的提问来源于stack exchange,提问作者Murali

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 13:08:08