簡體   English   中英

多生產者多消費者多線程Java

[英]Multiple Producer Multiple Consumer Multithreading Java

我正在嘗試多生產者 - 生產者 - 消費者問題的多個消費者使用案例。 我正在使用BlockingQueue在多個生產者/消費者之間共享公共隊列。

以下是我的代碼。
制片人

import java.util.concurrent.BlockingQueue;

public class Producer implements Runnable {

    private BlockingQueue inputQueue;
    private static volatile int i = 0;
    private volatile boolean isRunning = true;

    public Producer(BlockingQueue q){
        this.inputQueue=q;
    }

    public synchronized void run() {

        //produce messages
        for(i=0; i<10; i++) 
        {
            try {
                inputQueue.put(new Integer(i));

                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Produced "+i);
        }
        finish();
    }

    public void finish() {
        //you can also clear here if you wanted
        isRunning = false;
    }

}

消費者

import java.util.concurrent.BlockingQueue;

public class Consumer implements Runnable {

    private BlockingQueue inputQueue;
    private volatile boolean isRunning = true;

    private final Integer POISON_PILL = new Integer(-1);

    Consumer(BlockingQueue queue) {
        this.inputQueue = queue;
    }

    public void run() {
        //worker loop keeps taking en element from the queue as long as the producer is still running or as 
        //long as the queue is not empty:
        while(!inputQueue.isEmpty()) {

            try {
                Integer queueElement = (Integer) inputQueue.take();
                System.out.println("Consumed : " + queueElement.toString());

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("Queue ");
    }

    //this is used to signal from the main thread that he producer has finished adding stuff to the queue
    public void finish() {
        //you can also clear here if you wanted
        isRunning = false;
        inputQueue.add(POISON_PILL);
    }
}

測試類

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;


public class ProducerConsumerService {

    public static void main(String[] args) {

        //Creating BlockingQueue of size 10
        BlockingQueue queue = new ArrayBlockingQueue(10);

        Producer producer = new Producer(queue);
        Consumer consumer = new Consumer(queue);

        //starting producer to produce messages in queue
        new Thread(producer).start();

        //starting producer to produce messages in queue
        new Thread(producer).start();

        //starting consumer to consume messages from queue
        new Thread(consumer).start();

        //starting consumer to consume messages from queue
        new Thread(consumer).start();

        System.out.println("Producer and Consumer has been started");
    }

}

運行以下代碼時,我看不到正確的輸出。

我在這里做錯了嗎?

你的代碼中有相當一部分沒有意義。 我建議你坐下來弄清楚為什么代碼在那里以及它在做什么。

如果刪除了isFinshed標志,則不會發生任何變化。

如果您在生產者中刪除了synchronized的使用,那么您將擁有並發生產者。 創建僅在同步塊volatile中訪問的字段沒有任何好處。

對於生產者來說,共享循環計數器是沒有意義的,如果它們是並發的。 通常,生產者發送毒丸,而消費者不會消費葯丸。 例如,如果你有兩個消費者,一個可能會添加葯丸,另一個可能會消耗它。 您的消費者會忽略毒丸,因為它會忽略isFinished標志。

您不希望僅因為隊列暫時為空而停止使用者。 否則它將不會看到生產者產生的所有消息,可能沒有消息。

包含多個生產者和多個消費者的示例代碼

import java.util.concurrent.*;

public class ProducerConsumerDemo {

    public static void main(String args[]){

     BlockingQueue<Integer> sharedQueue = new LinkedBlockingQueue<Integer>();

     Thread prodThread1 = new Thread(new Producer(sharedQueue,1));
     Thread prodThread2 = new Thread(new Producer(sharedQueue,2));
     Thread consThread1 = new Thread(new Consumer(sharedQueue,1));
     Thread consThread2 = new Thread(new Consumer(sharedQueue,2));

     prodThread1.start();
     prodThread2.start();
     consThread1.start();
     consThread2.start();
    }

}

class Producer implements Runnable {

    private final BlockingQueue<Integer> sharedQueue;
    private int threadNo;

    public Producer(BlockingQueue<Integer> sharedQueue,int threadNo) {
        this.threadNo = threadNo;
        this.sharedQueue = sharedQueue;
    }

    @Override
    public void run() {
        for(int i=1; i<= 5; i++){
            try {
                int number = i+(10*threadNo);
                System.out.println("Produced:" + number + ":by thread:"+ threadNo);
                sharedQueue.put(number);
            } catch (Exception err) {
                err.printStackTrace();
            }
        }
    }

}

class Consumer implements Runnable{

    private final BlockingQueue<Integer> sharedQueue;
    private int threadNo;
    public Consumer (BlockingQueue<Integer> sharedQueue,int threadNo) {
        this.sharedQueue = sharedQueue;
        this.threadNo = threadNo;
    }

    @Override
    public void run() {
        while(true){
            try {
                int num = sharedQueue.take();
                System.out.println("Consumed: "+ num + ":by thread:"+threadNo);
            } catch (Exception err) {
               err.printStackTrace();
            }
        }
    }   
}

輸出:

Produced:11:by thread:1
Produced:21:by thread:2
Produced:22:by thread:2
Produced:23:by thread:2
Produced:24:by thread:2
Produced:25:by thread:2
Consumed: 11:by thread:1
Consumed: 22:by thread:1
Consumed: 23:by thread:1
Consumed: 24:by thread:1
Consumed: 25:by thread:1
Produced:12:by thread:1
Consumed: 21:by thread:2
Consumed: 12:by thread:1
Produced:13:by thread:1
Produced:14:by thread:1
Produced:15:by thread:1
Consumed: 13:by thread:2
Consumed: 14:by thread:1
Consumed: 15:by thread:2

本文提供了BlockingQueue的簡單示例生產者和消費者問題

您的代碼更改:

  1. 不同的生產者將生成不同的輸出而不是相同的輸出 生產者線程1生成11-15的數字,生產者線程2生成21-25的數字
  2. 任何Consumer線程都可以使用任何Producer中的數據。 與生產者不同,消費者沒有使用數據的約束。
  3. 我在Producer和Consumer中都添加了Thread號。

您可以在以下位置找到ExecutorService替代解決方案:

使用隊列的生產者/消費者線程

直接實施它並不太難。 下面的示例代碼就是這樣做的。 它只是將本地變量用於不應該共享的所有內容。

除隊列外,只共享一個保持活動生成器數量的線程安全計數器。 使用計數器而不是特殊的“ POISON_PILL ”值,因為這樣的標記值不適用於單個隊列和多個消費者,因為所有消費者必須識別生產者的完成但僅在所有生產者完成時。

計數器是一個簡單的結束條件。 唯一需要注意的是,在檢測到計數器達到零之后,必須重新檢查隊列以避免競爭條件。

作為旁注,使用Java 5提供的並發功能並且不使用Generics來實現干凈的類型安全代碼是沒有意義的。

final AtomicInteger activeProducers=new AtomicInteger();
final BlockingQueue<Integer> queue=new ArrayBlockingQueue<>(10);
Runnable producer=new Runnable() {
  public void run() {
    try {
      for(int i=0; i<10; i++) {
          Thread.sleep(TimeUnit.SECONDS.toMillis(1));
          queue.put(i);
          System.out.println("Produced "+i);
      }
    } catch(InterruptedException ex) {
      System.err.println("producer terminates early: "+ex);
    }
    finally { activeProducers.decrementAndGet(); }
  }
};
Runnable consumer=new Runnable() {
  public void run() {
    try {
      for(;;) {
        Integer queueElement = queue.poll(1, TimeUnit.SECONDS);
        if(queueElement!=null)
          System.out.println("Consumed : " + queueElement);
        else if(activeProducers.get()==0 && queue.peek()==null) return;
      }
    } catch(InterruptedException ex) {
      System.err.println("consumer terminates early: "+ex);
    }
  }
};
final int NUM_PRODUCERS = 2, NUM_CONSUMERS = 2;
for(int i=0; i<NUM_PRODUCERS; i++) {
  activeProducers.incrementAndGet();
  new Thread(producer).start();
}
for(int i=0; i<NUM_CONSUMERS; i++) {
  new Thread(consumer).start();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM