多线程生产者、消费者模式中,如何停止消费者

多线程生产者、消费者模式中,如何停止消费者 ?多生产者情况下对“毒丸”策略的应用

生产者、消费者模式是多线程中的经典问题。通过中间的缓冲队列,使得生产者和消费者的速度可以相互调节。

发散:一个主线程控制多个子线程,多个主线程控制多个子线程(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) {

    }
  }
}

 

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
多线程生产者消费者模式是一种常见的并发编程模式,用于解决生产者消费者之间的数据交互问题。在Java,可以使用多线程和相关的同步机制来实现生产者消费者模式。 在这种模式生产者负责生成数据,并将数据放入共享的缓冲区,而消费者则从缓冲区取出数据进行处理。为了保证线程安全和避免竞态条件,需要使用锁或其他同步机制来控制对共享缓冲区的访问。 以下是一个简单的Java多线程生产者消费者的示例代码: ```java import java.util.LinkedList; class ProducerConsumer { private LinkedList<Integer> buffer = new LinkedList<>(); private int capacity = 10; public void produce() throws InterruptedException { int value = 0; while (true) { synchronized (this) { while (buffer.size() == capacity) { wait(); } System.out.println("Producer produced: " + value); buffer.add(value++); notify(); Thread.sleep(1000); } } } public void consume() throws InterruptedException { while (true) { synchronized (this) { while (buffer.isEmpty()) { wait(); } int value = buffer.removeFirst(); System.out.println("Consumer consumed: " + value); notify(); Thread.sleep(1000); } } } } public class Main { public static void main(String[] args) { ProducerConsumer pc = new ProducerConsumer(); Thread producerThread = new Thread(() -> { try { pc.produce(); } catch (InterruptedException e) { e.printStackTrace(); } }); Thread consumerThread = new Thread(() -> { try { pc.consume(); } catch (InterruptedException e) { e.printStackTrace(); } }); producerThread.start(); consumerThread.start(); } } ``` 上述代码,ProducerConsumer类维护了一个缓冲区buffer和一个容量capacity。生产者通过调用produce方法向缓冲区添加数据,消费者通过调用consume方法从缓冲区取出数据。在生产者消费者的方法,使用synchronized关键字来保证同一时间只有一个线程能够访问共享资源,并使用wait和notify方法来实现线程间的通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值