多線程生產者、消費者模式中,如何停止消費者

多線程生產者、消費者模式中,如何停止消費者 ?多生產者情況下對“毒丸”策略的應用

生產者、消費者模式是多線程中的經典問題。通過中間的緩衝隊列,使得生產者和消費者的速度可以相互調節。

發散:一個主線程控制多個子線程,多個主線程控制多個子線程(countDownLunch使用理解)

對於比較常見的單生產者、多消費者的情況,主要有以下兩種策略:

  1. 通過volatile boolean producerDone =false 來標示是否完成。生產者結束後標示爲true, 消費者輪詢這個變量來決定自己是否退出。 這種方式對producerDone產生比較大的爭用,實現起來也有諸多問題需要考慮。

  2. 比較經典的“毒丸”策略,生產者結束後,把一個特別的對象:“毒丸”對象放入隊列。消費者從隊列中拿到對象後,判斷是否是毒丸對象。如果是普通非毒丸對象,則正常消費。如果是毒丸對象,則放回隊列(殺死其他消費者),然後結束自己。這種方式不會對結束狀態產生爭用,是比較好的方式。

 

由於“毒丸”策略是在單生產者多消費者情況下的。對於多生產者的情況,需要對之進行一些修改。我的想法是這樣的。用Countdownlatch作爲生產者計數器。所有生產者結束後,由協調者放入毒丸對象,消費者退出過程是一樣的。上代碼:

三個類 : 

Coordinator: 啓動生產者消費者,提供隊列、計數器。生產者全部結束後,放入毒丸。

Producer: 隨機生產和結束,結束前使countdownlatch + 1

Consumer: 判斷毒丸對象。如果是毒丸,放回隊列(殺死其他消費者),然後自己退出。

package com.XXX.XXX.XXX.poisonpill;

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

/**
 * @author mossGao
 * @Description 協調器 啓動生產者消費者,提供隊列、計數器。生產者全部結束後,放入毒丸。
 * @create 2020-03-27 16:37
 **/
public class Coordinator {

  public static final Object POISON_PILL = new Object();//special object to kill consumers
  private int productCount = 3;
  private int consumerCount = 5;

  public void startAll() throws Exception {
    BlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(20);
    CountDownLatch noMoreToProduce = new CountDownLatch(productCount);
    //start consumers;
    for (int i = 0; i < consumerCount; i++) {
      new Thread(new Consumer("consumer " + i, queue)).start();
    }
    //start producers;
    for (int i = 0; i < productCount; i++) {
      new Thread(new Producer("producer " + i, queue, noMoreToProduce)).start();
    }
    //wait until all producer down
    noMoreToProduce.await();
    System.out
        .println("All producer finished, putting POISON_PILL to the queue to stop consumers!");
    //put poison pill
    queue.put(POISON_PILL);
  }

  public static void main(String[] args) throws Exception {
    new Coordinator().startAll();
  }
}

 

 

package com.XXX.XXX.XXX.poisonpill;

import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;

/**
 * @author mossGao
 * @Description 隨機生產和結束,結束前使countdownlatch + 1
 * @create 2020-03-27 16:38
 **/
public class Producer implements Runnable {

  private String name;
  private CountDownLatch noMoreToProduce;
  private BlockingQueue<Object> queue;
  private Random random = new Random();


  public Producer(String name, BlockingQueue<Object> queue, CountDownLatch noMoreToProduce) {
    this.name = name;
    this.queue = queue;
    this.noMoreToProduce = noMoreToProduce;
  }

  @Override
  public void run() {
    System.out.println(name + " started.");
    try {
      while (true) {
        Object item = randomProduce();
        if (item == null) {
          break; //break if no more item
        }
        queue.put(item);
        System.out.println(name + " produced one.");
      }
    } catch (InterruptedException e) {
      //log
    } finally {
      System.out.println(name + " finished.");
      noMoreToProduce.countDown();//count down to signal "I finished."
    }
  }

  private Object randomProduce() {
    if (random.nextBoolean()) {
      return new Object();
    }
    return null;
  }
}

 

package com.XXX.XXX.XXX.poisonpill;

import java.util.concurrent.BlockingQueue;

/**
 * @author mossGao
 * @Description 判斷毒丸對象。如果是毒丸,放回隊列(殺死其他消費者),然後自己退出。
 * @create 2020-03-27 16:39
 **/
public class Consumer implements Runnable {

  private String name;
  private BlockingQueue<Object> queue;

  public Consumer(String name, BlockingQueue<Object> queue) {
    this.name = name;
    this.queue = queue;
  }

  @Override
  public void run() {
    try {
      System.out.println(name + " started.");
      while (true) {
        Object item = queue.take();
        //poison pill processing
        if (item == Coordinator.POISON_PILL) {
          queue.put(item);//put back to kill others
          System.out.println(name + " finished");
          break;
        }
        item = null;//pretend to consume the item;
        System.out.println(name + " consumed one");
      }
    } catch (InterruptedException e) {

    }
  }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章