繁体   English   中英

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

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

通常,现有的主题可以帮助我解决问题,但现在我发现自己陷入困境。

我想在Java中使用并发来实现Prod / Cons。 不使用现有API,因为它是出于学习目的。

我的生产者阻止消费者使用队列中的消息(Holder),但我希望Producer和Consumers同时使用队列。

您可以运行我的示例,您将看到,在Producer添加时,Consumer等待锁定。 但我希望消费者在添加消息后立即完成工作,而不是在制作人告诉他时。

我很惊讶我发现搜索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));
        }
    }
}

编辑:所以假设我们从这个等式中排除Thread.sleep。 如果我有100000个生产者,他们每个人都会发出消息。 他们不是阻止我的消费者吗? 因为持有人的常见锁定。 没有办法,也许是让我的消费者单独完成工作的另一种模式? 从我理解到现在,我的实施是正确的,我可能会尝试实现不可能的?

为了线程安全,消费者和生产者可能不会同时使用queur。 但是从队列中添加或删除应该是超高速的。 在一个现实的例子中,需要时间的是生成项目(例如获取网页)并使用它(例如解析它)。

你的sleep()调用应该在synchronized块之外:

  • 在生产者不使用队列时避免阻塞消费者;
  • 在生产者不使用队列时避免阻止其他生产者。

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();
    }
}

在任何实际情况中,你需要有一个平衡数量的生产者和消费者,否则,生产者明显增多,应用程序迟早会崩溃,因为堆已经搞砸了尚未消耗的生产物品。

对此的一个解决方案是拥有像ArrayBlockingQueue这样的有界队列。 在队列访问期间,消费者和生产者在很短的时间内阻止,但如果生产者疯狂,队列的容量将耗尽,生产者将进入等待状态,因此消费者可以赶上。

如果你有很多对单个队列的并发访问,并认为小的阻塞时间总和相关,你可以使用像ConcurrentLinkedQueue这样的非阻塞队列 - 不建议你自己尝试实现这样的数据结构。 在这里,消费者和生产者可以同时访问队列,但是,如果你的生产者生产的速度比消费者处理项目的速度快,那么没有什么能保护你免于堆积崩溃......

暂无
暂无

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

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