簡體   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