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
问题根源分析
notify()调用对象错误:生产者和消费者线程中直接调用notify(),等价于this.notify(),但线程持有的是Shop.LL的锁,必须调用Shop.LL.notify()才能唤醒等待该对象的线程,当前调用无法起到唤醒作用。- 线程执行逻辑缺陷:生产者生产后直接进入等待,消费者消费后也进入等待,两个线程都陷入等待状态,且没有其他线程唤醒它们;同时生产者的
if判断只会执行一次,无法循环生产,消费者也无循环消费逻辑,不符合模型的循环执行要求。 - 不必要的同步锁:
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
相关产品推荐
相关产品推荐

