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. 任何东西都是不好的模式。如何使用itemCountbufferSize 创建一个对象,然后将该对象的相同实例传递给我们所有的生产者和消费者?它也是你要锁定的对象。

按照其他人的建议,小心使用System.out.println(...) 消息散布您的线程代码。 System.out 是一个同步类,因此这将添加可能移动或修复问题的锁和内存同步。是的。调试线程程序是困难

【讨论】:

以上是关于java线程同步中没有信号量的生产者消费者问题的主要内容,如果未能解决你的问题,请参考以下文章

linux系统编程:线程同步-信号量(semaphore)

Linux:详解多线程(线程同步信号量和生产者与消费者模型的实现)

Linux:详解多线程(线程同步信号量和生产者与消费者模型的实现)

多线程--信号量

用c语言或c++编写编程实现生产者消费者或读写者的同步问题

linuxbingc(多线程)