生产者与消费者的实现

生产者与消费者的实现

一到多个线程充当生产者,生产元素。一到多个线程充当消费者,消费元素。在两者之间插入一个队列充当缓冲区,建立起生产者和消费者的松散耦合。

正常情况下,即生产元素的速度和消费元素的速度差不多时,生产者和消费者其实是不需要去关注对方的。

生产者可以一直生产,因为队列里总是有空间。消费者可以一直消费,因为队列里总是有元素。即达到一个动态的平衡。

但在特殊情况下,比如生产元素的速度很快,队列里没有了空间,此时生产者必须自我“停工”,开始“休息”。

一旦消费者消费了元素之后,队列里才会有空间,生产者才可以重启生产,所以,消费者在消费完元素后有义务去叫醒生产者复工。

更准确的说法应该是,只有在生产者“休息”时,消费者消费完元素后才需要去叫醒生产者。否则,其实可以不用叫醒,因为人家本来就没休息。

反之,如果消费元素的速度很快,队列里没有了元素,只需把上述情况颠倒过来即可。

但这样的话就会引入一个新的问题,就是要能够准备的判断出对方有没有在休息,为此就必须定义一个状态变量,在自己即将开始休息时,自己设置下这个变量。

对方通过检测这个变量,来决定是否进行叫醒操作。当自己被叫醒后,首先要做的就是清除一下这个变量,表明我已经醒来复工了。

这样就需要多维护一个变量和多了一部分判断逻辑。可能有些人会觉得可以通过判断队列的“空”或“满”(即队列中的元素数目)来决定是否进行叫醒操作。

在高并发下,可能刚刚判断队列不为空,瞬间之后队列可能已经变为空的了,这样会导致逻辑出错。线程可能永远无法被叫醒。

因此,综合所有,生产者每生产一个元素后,都会通知消费者,“现在有元素的,你可以消费”。

同样,消费者每消费一个元素后,也会通知生产者,“现在有空间的,你可以生产”。

很明显,这些通知很多时候(即对方没有休息时)是没有真正意义的,不过无所谓,只要忽略它们就行了。

首先要保证是正确的,“宁可错杀一千,也不放过一个”。

 

常见的生产者与消费者的实现有以下几种:

  • Synchronized的wait 和notifyAll;
  • ReentrantLock的await 和signalAll;
  • 阻塞队列ArrayBlockingQueue等。

 

下面以lock实现简单生产者和消费者:

 public static void main(String[] args) {
    Queue queue = new Queue();
    new Thread(new Producer(queue)).start();
    new Thread(new Producer(queue)).start();
    new Thread(new Consumer(queue)).start();
  }
  

  /**
   *
   * 生产者
   *
   */
  static class Producer implements Runnable {
    Queue queue;
    
    Producer(Queue queue) {
      this.queue = queue;
    }
    
    @Override
    public void run() {
      try {
        for (int i = 0; i < 10000; i++) {
          doingLongTime();
          queue.putEle(random(10000));
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    
  }


  /**
   *
   * 消费者
   *
   */
  static class Consumer implements Runnable {

    Queue queue;
    
    Consumer(Queue queue) {
      this.queue = queue;
    }
    
    @Override
    public void run() {
      try {
        for (int i = 0; i < 10000; i++) {
          doingLongTime();
          queue.takeEle();
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    
  }

  /**
   *
   * 生产队列
   *
   */
  static class Queue {
    Lock lock = new ReentrantLock();
    Condition prodCond  = lock.newCondition();
    Condition consCond = lock.newCondition();
    
    final int capacity= 20;
    Object[] container = new Object[capacity];
    int count = 0;
    int putIndex = 0;
    int takeIndex = 0;
    
    public void putEle(Object ele) throws InterruptedException {
      try {
        lock.lock();
        while (count == capacity) {
          println("队列已满:%d,生产者开始休息。。。", count);
          prodCond.await();
        }
        container[putIndex] = ele;
        println("生产元素:%d", ele);
        putIndex++;
        if (putIndex >= capacity) {
          putIndex = 0;
        }
        count++;
        println("通知消费者去消费。。。");
        consCond.signalAll();
      } finally {
        lock.unlock();
      }
    }
    
    public Object takeEle() throws InterruptedException {
      try {
        lock.lock();
        while (count == 0) {
          println("队列已空:%d,消费者开始休息。。。", count);
          consCond.await();
        }
        Object ele = container[takeIndex];
        println("消费元素:%d", ele);
        takeIndex++;
        if (takeIndex >= capacity) {
          takeIndex = 0;
        }
        count--;
        println("通知生产者去生产。。。");
        prodCond.signalAll();
        return ele;
      } finally {
        lock.unlock();
      }
    }
  }
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值