java线程同步中没有信号量的生产者消费者问题
Posted
技术标签:
【中文标题】java线程同步中没有信号量的生产者消费者问题【英文标题】:Producer consumer problems without semaphores in java threads synchronization 【发布时间】:2017-02-02 19:29:33 【问题描述】:您好,我一直在尝试在没有信号量的情况下解决 java 中的生产者消费者问题。当我使用单一生产者和单一消费者时,我的代码工作正常。但是当我添加多个消费者时,它就完全搞砸了,所有消费者线程都进入同步块。我不确定为什么会这样。这是我的代码:
生产者类:
public class Producer implements Runnable
Object SharedObject = null;
String producerName= null;
Random rn = new Random();
public Producer(Main m, String s)
this.SharedObject = m;
this.producerName=s;
public Producer(Main m)
this.SharedObject = m;
public void run()
while (true)
synchronized (SharedObject)
if (Main.itemCount == Main.bufferSize)
try
System.out.println("Producer is sleeping and waiting for notification form Consumer");
SharedObject.wait();
catch (InterruptedException e)
e.printStackTrace();
Main.itemCount++;
System.out.println(this.producerName+" Produced the item and the item count is : " + Main.itemCount);
if (Main.itemCount == 1)
SharedObject.notify();
System.out.println("Producer Notified the cosumer to wake up");
try
int i = rn.nextInt(100);
Thread.sleep(i);
catch (Exception e)
e.printStackTrace();
消费类:
public class Consumer implements Runnable
Object SharedObject = null;
String consumerName= null;
Random rn = new Random();
public Consumer(Main m, String s)
SharedObject = m;
this.consumerName=s;
Consumer c= new Consumer((Main) SharedObject,consumerName);
synchronized void consume()
synchronized (SharedObject)
if (Main.itemCount == 0)
try
System.out.println(this.consumerName+" is sleeping and waiting for notify from Producer");
SharedObject.wait();
catch (InterruptedException e)
e.printStackTrace();
Main.itemCount--;
System.out.println(this.consumerName+" consumed 1 item and the item Count is " + Main.itemCount);
if (Main.itemCount == 4)
SharedObject.notifyAll();
System.out.println("Consumer notified the producer to wake up");
public void run()
while (true)
c.consume();
try
int i = rn.nextInt(100);
Thread.sleep(i);
catch (Exception e)
e.printStackTrace();
主类:
public class Main
static int itemCount = 0;
static int bufferSize = 5;
public static void main(String[] args)
Main m = new Main();
Thread objP = new Thread(new Producer(m, "Producer1"));
Thread objC = new Thread(new Consumer(m, "Consumer1"));
Thread objC2 = new Thread(new Consumer(m, "Consumer2"));
Thread objC3 = new Thread(new Consumer(m, "Consumer3"));
objP.start();
objC.start();
objC2.start();
objC3.start();
【问题讨论】:
你不知道类和对象 - 你是如何进步到这个级别的?! 我不确定是什么给了您这样的印象,但我真的很想知道代码中出现的错误。谢谢。 【参考方案1】:您在生产者中使用notifyAll
,这会唤醒所有在监视器上等待的消费者线程。如果你只想唤醒一个消费者,你应该使用notify
来自API documentation:
notify()
唤醒正在此对象的监视器上等待的单个线程。
notifyAll()
唤醒所有在这个对象的监视器上等待的线程。
您的消费者最好在醒来时实际检查他们是否可以使用资源。如果你想继续使用notifyAll
,消费者应该可以被唤醒,如果可用资源不足,则返回等待。
我建议打印main.itemCount
。这将使您更清楚您遇到的问题。
拨打notify
的时候要注意。
为什么你的producer
只在只有一件物品可用时才调用notify
?生产者不应该在有可用物品时致电notify
吗?
consumer
只告诉producer
在有 4 个项目时唤醒(这不是满了吗?)。
【讨论】:
您好,感谢您的回复,我将生产者的监视器更改为 notify() ,即使这样也无法获得预期的结果。当我使用 notifyAll() 时,我认为即使所有消费者都被它唤醒,因为我们有同步块,我假设在给定时间只有一个线程会在块内。现在我将其更改为 notify() 但无法继续... @CaptainCold 更新了答案。您的实施存在更多问题。我在回答为什么你所有的消费者线程都被唤醒了。 根据我的代码,只有当商品计数为“0”时,消费者才会进入等待阶段,所以我只是添加了条件来通知商品计数何时变为“1”,因为当计数是“0”以外的任何内容,消费者不会等待。和生产者类似的事情,生产者只有在缓冲区满时才进入睡眠阶段,即 5,所以我们只需要在消费者消费一个项目时唤醒生产者,使项目计数为 4,一旦项目计数为4 生产者醒来并继续试图获得锁的控制权以添加项目。与此同时,即使消费者...... 再次消费,计数将减少到 3、2 和 1,但在所有这些情况下,生产者已经收到通知并正在尝试获取锁。@mattm【参考方案2】:实际上将 notifyAll() 更改为 notify() kindoff 有效!!!谢谢你的建议。这是我的代码:
生产者类:
package com.source;
导入 java.util.Random;
public 类 Producer 实现 Runnable
Object SharedObject = null;
String producerName = null;
Random rn = new Random();
public Producer(Main m, String s)
this.SharedObject = m;
this.producerName = s;
public Producer(Main m)
this.SharedObject = m;
public void run()
while (true)
synchronized (SharedObject)
if (Main.itemCount == Main.bufferSize)
try
System.out
.println(this.producerName + "is sleeping and waiting for notification form Consumer");
SharedObject.wait();
catch (InterruptedException e)
e.printStackTrace();
Main.itemCount++;
System.out.println(this.producerName + " Produced the item and the item count is : " + Main.itemCount);
if (Main.itemCount == 1)
SharedObject.notify();
System.out.println("Producer Notified the cosumer to wake up");
try
int i = rn.nextInt(100);
Thread.sleep(i);
catch (Exception e)
e.printStackTrace();
消费类:
package com.source;
导入 java.util.Random;
公共类 Consumer 实现 Runnable
Object SharedObject = null;
String consumerName = null;
Random rn = new Random();
public Consumer(Main m, String s)
SharedObject = m;
this.consumerName = s;
public void run()
while (true)
synchronized (SharedObject)
if (Main.itemCount == 0)
try
System.out.println(this.consumerName + " is sleeping and waiting for notify from Producer");
SharedObject.wait();
catch (InterruptedException e)
e.printStackTrace();
Main.itemCount--;
System.out.println(this.consumerName + " consumed 1 item and the item Count is " + Main.itemCount);
if (Main.itemCount == 4)
SharedObject.notify();
System.out.println("Consumer notified the producer to wake up");
try
int i = rn.nextInt(1000);
Thread.sleep(i);
catch (Exception e)
e.printStackTrace();
主类:
package com.source;
公共类主
static int itemCount = 0;
static int bufferSize = 5;
public static void main(String[] args)
Main m = new Main();
Thread objP = new Thread(new Producer(m, "Producer1"));
Thread objC = new Thread(new Consumer(m, "Consumer1"));
Thread objC2 = new Thread(new Consumer(m, "Consumer2"));
Thread objC3 = new Thread(new Consumer(m, "Consumer3"));
Thread objP2 = new Thread(new Producer(m, "Producer2"));
Thread objP3 = new Thread(new Producer(m, "Producer3"));
objP.start();
objC.start();
objC2.start();
objC3.start();
objP2.start();
objP3.start();
再次感谢大家宝贵的时间和建议。
【讨论】:
【参考方案3】:听起来您已经解决了最初的问题,但这里有更多反馈。
我相信您真正的问题不是因为notifyAll()
,而是因为您的缓冲区测试是if
测试而不是while
循环。在经典的竞争条件下,线程被唤醒但缓冲区中没有 no 元素。见my notes here。所以你的代码应该是这样的:
while (Main.itemCount == Main.bufferSize)
和
while (Main.itemCount == 0)
调用notifyAll()
加剧了问题,但即使只使用notify()
,竞争条件仍然存在。随着您添加更多消费者或其他生产者,您会发现更多问题。
这里有一些其他反馈。
非常小心锁中的锁。这通常是一种糟糕的模式,而且我很少使用。你真的需要consume()
进行同步吗?
对象实例名称应以小写字母开头,因此应为sharedObject
。
如果可能,您锁定的任何对象都应该是private final
。您不希望它更改为另一个对象。
使用Main.
任何东西都是不好的模式。如何使用itemCount
和bufferSize
创建一个对象,然后将该对象的相同实例传递给我们所有的生产者和消费者?它也是你要锁定的对象。
按照其他人的建议,小心使用System.out.println(...)
消息散布您的线程代码。 System.out
是一个同步类,因此这将添加可能移动或修复问题的锁和内存同步。是的。调试线程程序是困难。
【讨论】:
以上是关于java线程同步中没有信号量的生产者消费者问题的主要内容,如果未能解决你的问题,请参考以下文章
Linux:详解多线程(线程同步信号量和生产者与消费者模型的实现)