【问题标题】:Producer consumer problems without semaphores in java threads synchronizationjava线程同步中没有信号量的生产者消费者问题
【发布时间】: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();
    }
}

【问题讨论】:

  • 你不知道类和对象 - 你是如何进步到这个级别的?!
  • 我不确定是什么给了您这样的印象,但我真的很想知道代码中出现的错误。谢谢。

标签: java multithreading producer-consumer


【解决方案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 是一个同步类,因此这将添加可能移动或修复问题的锁和内存同步。是的。调试线程程序是困难

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多