简体   繁体   English

Java中的生产者/消费者 - 生产者阻止我的消费者

[英]Producer / Consumer in Java - Producer blocks my Consumer

Usually SO existent topics help me to get over a problem, but now I found myself stuck. 通常,现有的主题可以帮助我解决问题,但现在我发现自己陷入困境。

I want to implement a Prod/Cons using concurrency in Java. 我想在Java中使用并发来实现Prod / Cons。 Without using existing APIs because is for learning purposes. 不使用现有API,因为它是出于学习目的。

My Producers are blocking the Consumers to consume the messages from the queue (Holder) but I want Producer and Consumers to use the queue simultaneous. 我的生产者阻止消费者使用队列中的消息(Holder),但我希望Producer和Consumers同时使用队列。

You can run my sample and you will see that, while the Producer is adding, the Consumer waits for the lock. 您可以运行我的示例,您将看到,在Producer添加时,Consumer等待锁定。 But I want the consumer to do his job right after a message is added, not when the producer tells him. 但我希望消费者在添加消息后立即完成工作,而不是在制作人告诉他时。

I'm surprised that all those examples I found searching the P/C pattern works as mine (producer blocks the consumer, which doesn't make sense to me) 我很惊讶我发现搜索P / C模式的所有例子都是我的(生产者阻止消费者,这对我来说没有意义)

import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

class Holder<T> {

    private int capacity;
    private Queue<T> items = new LinkedList<T>();

    public Holder(int capacity) {
        this.capacity = capacity;
    }

    public synchronized void addItem(T item) throws InterruptedException {
        Thread.sleep(new Random().nextInt(2000));
        while (isFull()) {
            System.out.println("Holder FULL. adding operation is waiting... [" + item + "]");
            this.wait();
        }
        System.out.println(items.size() + "  -- holder +++ added " + item);
        items.add(item);
        this.notifyAll();
    }

    public T getItem() throws InterruptedException {
        synchronized (this) {
            while (isEmpty()) {
                System.out.println("Holder EMPTY. getting operation is waiting...");
                this.wait();
            }
            T next = items.poll();
            System.out.println(items.size() + "  -- holder --- removed " + next + " - remaining: " + items.size());
            this.notifyAll();
            return next;
        }
    }

    private synchronized boolean isEmpty() {
        return items.isEmpty();
    }

    private synchronized boolean isFull() {
        return items.size() >= capacity;
    }

}

class Producer implements Runnable {

    public static final int GENERATED_ITEMS_COUNT = 10;
    private int id;
    private Holder<String> holder;

    public Producer(int id, Holder<String> holder) {
        this.id = id;
        this.holder = holder;
    }

    @Override
    public void run() {
        try {
            for (int i = 0; i < GENERATED_ITEMS_COUNT; i++) {
                String produced = "Message " + i + " from [P" + id + "] " + System.nanoTime();
                holder.addItem(produced);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();

        }
    }
}

class Consumer implements Runnable {

    private Holder<String> holder;

    public Consumer(Holder<String> hodler) {
        this.holder = hodler;
    }

    @Override
    public void run() {
        while (true) {
            try {
                String consumed = holder.getItem();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ConsumerProducerApp {

    public static void main(String[] args) throws InterruptedException {
        Holder<String> coada = new Holder<String>(10);

        Thread consumer = new Thread(new Consumer(coada));
        consumer.start();

        Executor executor = Executors.newCachedThreadPool();
        for (int i = 1; i <= 9; i++) {
            executor.execute(new Producer(i, coada));
        }
    }
}

EDIT: So presuming we exclude the Thread.sleep from this equation. 编辑:所以假设我们从这个等式中排除Thread.sleep。 What if I have 100000 Producers, and they each produce messages. 如果我有100000个生产者,他们每个人都会发出消息。 Are not they blocking my Consumer ? 他们不是阻止我的消费者吗? because of that common lock on the Holder. 因为持有人的常见锁定。 Isn't any way, maybe another pattern that let my Consumer do his job individually ? 没有办法,也许是让我的消费者单独完成工作的另一种模式? From what I understand until now, my implementation is correct and I may try to achieve the impossible ? 从我理解到现在,我的实施是正确的,我可能会尝试实现不可能的?

To be thread-safe, the consumer and the producer may not use the queur concurrently. 为了线程安全,消费者和生产者可能不会同时使用queur。 But adding or removing from the queue should be superfast. 但是从队列中添加或删除应该是超高速的。 In a realistic example, what takes time is to produce the item (fetch a web page for example), and to consume it (parse it for example). 在一个现实的例子中,需要时间的是生成项目(例如获取网页)并使用它(例如解析它)。

Your sleep() call should be outside of the synchronized block: 你的sleep()调用应该在synchronized块之外:

  • to avoid blocking the consumer while the producer is not using the queue; 在生产者不使用队列时避免阻塞消费者;
  • to avoid blocking other producers while the producer is not using the queue. 在生产者不使用队列时避免阻止其他生产者。

.

public void addItem(T item) throws InterruptedException {
    // simulating long work, not using the queue
    Thread.sleep(new Random().nextInt(2000));

    // long work done, now use the queue
    synchronized (this) {
        while (isFull()) {
            System.out.println("Holder FULL. adding operation is waiting... [" + item + "]");
            this.wait();
        }
        System.out.println(items.size() + "  -- holder +++ added " + item);
        items.add(item);
        this.notifyAll();
    }
}

In any practical scenario, you need to have a balanced number of producers and consumers as otherwise, with significantly more producers, the application will collapse sooner or later due to the heap messed up with produced items which have not consumed yet. 在任何实际情况中,你需要有一个平衡数量的生产者和消费者,否则,生产者明显增多,应用程序迟早会崩溃,因为堆已经搞砸了尚未消耗的生产物品。

One solution to this is to have a bounded queue like ArrayBlockingQueue . 对此的一个解决方案是拥有像ArrayBlockingQueue这样的有界队列。 Consumers and producers are blocked during queue access for a tiny time fraction, but if the producers are going wild, the queue's capacity will become exhausted and the producers will go into the wait state, hence consumers can catch up then. 在队列访问期间,消费者和生产者在很短的时间内阻止,但如果生产者疯狂,队列的容量将耗尽,生产者将进入等待状态,因此消费者可以赶上。

If you have a lot of concurrent access to a single queue, and think the small blocked times sum up to become relevant, you may use a non-blocking queue like ConcurrentLinkedQueue — it's not recommended to try to implement such a data structure yourself. 如果你有很多对单个队列的并发访问,并认为小的阻塞时间总和相关,你可以使用像ConcurrentLinkedQueue这样的非阻塞队列 - 不建议你自己尝试实现这样的数据结构。 Here, consumers and producers can access the queue concurrently, however, nothing protects you from filling your heap to collapse if your producers produce faster than the consumers process the items… 在这里,消费者和生产者可以同时访问队列,但是,如果你的生产者生产的速度比消费者处理项目的速度快,那么没有什么能保护你免于堆积崩溃......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM