Java一个生产者和两个消费者

时间:2018-04-24 12:10:58

标签: java multithreading

我有一个制作人和两个消费者。我想展示消费者如何从生产者那里获取价值并展示它们。 问题是在我的代码中只有第二个消费者显示了生产者的项目。 怎么解决这个? 这是问题:

  public static void main(String[] args) throws Exception {
    // Object of a class that has both produce()
    // and consume() methods
    final ProdConsumer pc = new ProdConsumer();

    // Create producer thread
    Thread t1 = new Thread(new Runnable() {
        public void run() {
            try {
                pc.produce();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    // Create consumer thread
    Thread t2 = new Thread(new Runnable() {
        public void run() {
            try {
                pc.consume(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    Thread t3 = new Thread(new Runnable() {
        public void run() {
            try {
                pc.consume(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    // Start both threads
    t1.start();
    t2.start();
    t3.start();

//        // t1 finishes before t2
        t1.join();
        t2.join();
        t3.join();
    }

和producer_consumer类:

 public  class ProdCons
    {
        // Create a list shared by producer and consumer
        // Size of list is 2.
        LinkedList<Integer> list = new LinkedList<Integer>();
        int capacity = 2;

// Function called by producer thread
public void produce() throws InterruptedException
{
    int value = 0;
    while (true)
    {
        synchronized (this)
        {
            // producer thread waits while list
            // is full
            while (list.size()==capacity)
                wait();

            System.out.println("Producer produced-"
                    + value);

            // to insert the jobs in the list
            list.add(value++);

            // notifies the consumer thread that
            // now it can start consuming
            notify();

            // makes the working of program easier
            // to  understand
            Thread.sleep(1000);
        }
    }
}

// Function called by consumer thread
public void consume(int thread) throws InterruptedException
{
    while (true)
    {
        synchronized (this)
        {
            // consumer thread waits while list
            // is empty
            while (list.size()==0)
                wait();

            //to retrive the ifrst job in the list
            int val = list.removeFirst();

            System.out.println("Consumer" + thread + " consumed-"
                    + val);

            // Wake up producer thread
            notify();

            // and sleep
            Thread.sleep(1000);
        }
    }
}

}

谢谢 我错过了什么?

6 个答案:

答案 0 :(得分:2)

wait/notify机制不是fair,这意味着如果有两个线程在等待同一个资源,那么当您调用notify()时,可以通知其中任何一个。这有时是starvation problem的问题。

因此,在您第一次通知的情况下,例如,第一个消费者获得此通知,然后在完成作业后再次调用wait,这意味着第二次生成者调用{{1}你又有两个消费者在等待,然后无法保证它会唤醒另一个消费者,它可能是其中任何消费者。

如果您将减少生产者的notify金额,将会减少此问题,而不是消费者。实际上可能是它甚至不是问题,因为在你的情况下消费者的吞吐量与生产者相同,所以基本上你不需要第二个消费者,但它在现实生活中是罕见的情况,所以要模仿你的情况两个消费者都在工作,你应该增加生产者的吞吐量。

但是在我看来,在使用像Thread.sleep这样的低级机制之前,你应该考虑一下。例如,查看BlockingQueue或java中的其他并发原语。例如,您可以使ArrayBlockingQueue公平:

  

Java doc:此类支持用于排序等待生产者和消费者线程的可选公平策略。默认情况下,不保证此顺序。但是,将fairness设置为true构造的队列以FIFO顺序授予线程访问权限。公平性通常会降低吞吐量,但会降低可变性并避免饥饿。

因此,代替列表,您将拥有此队列,并且在此队列上调用wait/notify时,您将获取要使用的队列中的下一个元素,或者如果它为空,则您的线程将阻塞,直到将存在新元素。 并且将take标志指定为true意味着它将使用fair来唤醒下一个消费者。

所以你的代码看起来像是:

FIFO

此外,您可能会对本文解释饥饿和public class ProdCons { // Create a queue shared by producer and consumer // Size of list is 2. BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(2, true); int capacity = 2; // Function called by producer thread public void produce() throws InterruptedException { int value = 0; while (true) { System.out.println("Producer produced-" + value); // to insert the jobs in the queue // will block in case there is no more space in a queue queue.put(value++); // and sleep Thread.sleep(500); } } // Function called by consumer thread public void consume(int thread) throws InterruptedException { while (true) { //retrieve the first job in the queue //will block in case queue is empty, until its not empty int val = queue.take(); System.out.println("Consumer" + thread + " consumed-" + val); // and sleep Thread.sleep(1000); } } } 公平性感兴趣:http://tutorials.jenkov.com/java-concurrency/starvation-and-fairness.html

答案 1 :(得分:0)

为了说明我对不使用wait / notify的评论,这里是一个BlockingQueue的生产者/消费者。很抱歉,如果这实际上没有回答您关于代码无效的问题。

static final AtomicInteger value = new AtomicInteger();
public static void main(String[] args) {
    BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(2);

    Thread producer = new Thread(() -> { queue.put(value.getAndIncrement()) });
    producer.start();

    Runnable consumer1 = () -> {
        try { 
            while(true) {
               System.out.println("Consumer 1 consuming " + queue.take());
                Thread.sleep(200); 
            }
        }{ catch(Exception e) {}
    };
    Runnable consumer2 = () -> {
        try { 
            while(true) {
               System.out.println("Consumer 2 consuming " + queue.take());
                Thread.sleep(200); 
            }
        }{ catch(Exception e) {}
    };
    new Thread(consumer1).start();
    new Thread(consumer2).start();
}

旁注,我通常不会直接创建Thread个对象,而是使用ExecutorService代替,但这不是重点。

答案 2 :(得分:0)

我想通过发布和订阅使用java消息队列(JMS)以不同的方式解决这个问题。发布/订阅消息传递域是一对多模型,其中一个发布者通过主题将消息发送给所有活动的订阅者并且他们通过主题接收消息。它简单易行。这是细节。 https://howtodoinjava.com/jms/jms-java-message-service-tutorial/

答案 3 :(得分:0)

t1.join t2.join t3.join只允许主线程等待t1,t2,t3生产者和消费者线程完成。在这种情况下,所有线程都在while循环中运行,因此join调用没有任何区别。此外,如果未执行该线程中的同步块,则线程不会等待。根据谁首先获得锁定,同步块将被执行。

答案 4 :(得分:0)

首先,您需要使用.notifyAll(),而不是.notify()(如果一个消费者通知其他消费者,生产者永远不会醒来,这可能会很糟糕。)

其次,数据不会发送到2个列表,但只有一个,消费者正在争取从同一个地方获取; java总是说在睡眠/等待/同步等情况下没有可预测的线程调度...只有一个消费者和同一个重复唤醒是在规范内。

您需要使用ReentrantLock(true)进行公平锁定/唤醒。

答案 5 :(得分:-1)

每个线程都在自身(this)上进行同步,每个线程的线程都不同,因此不会阻止它们同时运行。由于它们(应该是)操纵跨线程共享的列表,因此它们应该在该列表(或其他一些共享锁对象)上进行同步。而且,更有问题的是,看起来每个线程都创建了自己的List - 它们不会共享列表。 List应该是静态(类)列表,或者应该传递给每个线程。