J.U.C--集合--生产者与消费者队列BlockingQueue

生产者与消费者队列

通过wait()和notifyAll()可以解决任务之间互相操作的问题,但是这是一种比较低级的方式,每次交互都必须握手,过程十分复杂。更好的方法是我们使用同步队列来解决任务之间相互协作的问题。

同步队列在任何时刻都只允许一个任务插入或则删除元素,并且在队列为空时任务自动阻塞。java.util.concurrent.BlockingQueue接口提供了这个队列,并给出了几个标准实现。一般我们可以使用LinkedBlockingQueue,这是一个无界队列,LinkedBlockingQueue底层用链表实现。 或则是ArrayBlockingQueue,它具有固定的尺寸,因此在阻塞之前可以放置有限数量的元素。

java.util.concurrent包中实现的各种阻塞队列都包含了足够的内部同步机制,从而安全的将对象从生产者线程发布到消费者线程

下面是一个典型的生产者和消费者队列的案例:

package thread;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.*;
import java.util.*;
import static thread.Print.*;

/***
 * BlockingQueue例子:一台机器有三个任务,
 * A制作吐司,B给吐司抹黄油,C给吐司末果酱,
 * 制作的顺序必须是A-》B-》C
 * 阻塞队列里面保存的应该是吐司对象,并且这里需要3个阻塞队列,分别保存:
 * (1)制作好了的吐司但没抹黄油的队列
 * (2)摸了黄油还没摸黄油的队列
 * (3)制作完成的队列
 */
class Toast {
    /**
     * 枚举表示吐司制作的几个过程
     */
  public enum Status {
      DRY, BUTTERED, JAMMED
  }
    //设置初试状态
  private Status status = Status.DRY;
    //设置每个吐司的唯一id
  private final int id;
  public Toast(int idn) {
      this.id = idn;
  }
    //涂黄油
  public void butter() {
      this.status = Status.BUTTERED;
  }
    //涂果酱
  public void jam() {
      status = Status.JAMMED;
  }
    //获取当前吐司的状态
  public Status getStatus() {
      return status;
  }
    //获取当前吐司的id
  public int getId() {
      return id;
  }
  public String toString() {
    return "Toast " + id + ": " + status;
  }
}

/**
 * 吐司的阻塞队列,队列里面的元素对象就是吐司对象,三个任务之间维护的就是吐司
 */
class ToastQueue extends LinkedBlockingQueue<Toast> {

}

/**
 * 制作吐司的线程
 * 这个线程只需要与已做好的吐司队列这一个队列交互
 * 将做好的吐司存放在吐司队列中,
 * 通过构造器传入队列
 */
class Toaster implements Runnable {
  private ToastQueue toastQueue;//构造器将会传入的阻塞队列对象
  private int count = 0;//
  private Random rand = new Random(47);
  public Toaster(ToastQueue tq) {
      this.toastQueue = tq;
  }

    @Override
  public void run() {
    try {
      while(!Thread.interrupted()) {
          //制作吐司的时间消耗
        //TimeUnit.MILLISECONDS.sleep(100 + rand.nextInt(100));
          TimeUnit.MILLISECONDS.sleep(2);
        //创建吐司对象
        Toast t = new Toast(count++);
        print(t);
        //将新建的吐司放在队列中
        toastQueue.put(t);
      }
    } catch(InterruptedException e) {
      print("Toaster interrupted");
    }
    print("Toaster off");
  }
}

/**
 * 给吐司抹黄油的线程
 * 这个任务设计到两个队列,首先从吐司队列中能取出吐司,然后再抹黄油。
 */
class Butterer implements Runnable {
  private ToastQueue dryQueue, butteredQueue;
  public Butterer(ToastQueue dry, ToastQueue buttered) {
    this.dryQueue = dry;
    this.butteredQueue = buttered;
  }
  public void run() {
    try {
      while(!Thread.interrupted()) {
        //阻塞等待吐司队列中有可以抹黄油的吐司对象
        Toast t = dryQueue.take();
          //抹黄油 500ms
          TimeUnit.MILLISECONDS.sleep(10);
        t.butter();
        print(t);
        butteredQueue.put(t);
      }
    } catch(InterruptedException e) {
      print("Butterer interrupted");
    }
    print("Butterer off");
  }
}

//给吐司抹上果酱的线程
//这个任务设计到两个队列,首先从黄油队列中取出,然后抹果酱,加入果酱队列(也就是成品了)。
class Jammer implements Runnable {
  private ToastQueue butteredQueue, finishedQueue;
  public Jammer(ToastQueue buttered, ToastQueue finished) {
    this.butteredQueue = buttered;
      this.finishedQueue = finished;
  }
  public void run() {
    try {
      while(!Thread.interrupted()) {
        // Blocks until next piece of toast is available:
        Toast t = butteredQueue.take();
          //抹果酱 500ms
          TimeUnit.MILLISECONDS.sleep(10);
        t.jam();
        print(t);
        finishedQueue.put(t);
      }
    } catch(InterruptedException e) {
      print("Jammer interrupted");
    }
    print("Jammer off");
  }
}

//最后的吃吐司的线程,字设计到
class Eater implements Runnable {

  private ToastQueue finishedQueue,eatedQueue;
  private int counter = 0;
  public Eater(ToastQueue finished, ToastQueue eated) {
    finishedQueue = finished;
      this.eatedQueue = eated;
  }
  public void run() {
    try {
      while(!Thread.interrupted()) {
        // Blocks until next piece of toast is available:
        Toast t = finishedQueue.take();
          TimeUnit.MILLISECONDS.sleep(100);//吃
          eatedQueue.put(t);//装入吃完了的队列
        // Verify that the toast is coming in order,
        // and that all pieces are getting jammed:
        if(t.getId() != counter++ ||
           t.getStatus() != Toast.Status.JAMMED) {
          print(">>>> Error: " + t);
          System.exit(1);
        } else
          print("Chomp! " + t);
      }
    } catch(InterruptedException e) {
      print("Eater interrupted");
    }
    print("Eater off");
  }
}


/**
 * 最后的main主线程
 */
public class ToastOMatic {
    public static void main(String[] args) throws Exception {

        //这里定义了三个阻塞队列
        //A是制作吐司的队列
        //B是抹黄油的队列
        //C是摸果酱的队列
        ToastQueue dryQueue = new ToastQueue();
        ToastQueue butteredQueue = new ToastQueue();
        ToastQueue finishedQueue = new ToastQueue();
        ToastQueue eatedQueue = new ToastQueue();
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new Toaster(dryQueue));
        exec.execute(new Toaster(dryQueue));
        exec.execute(new Toaster(dryQueue));//三个人做吐司

        exec.execute(new Butterer(dryQueue, butteredQueue));
        exec.execute(new Jammer(butteredQueue, finishedQueue));
        //三个人吃
        exec.execute(new Eater(finishedQueue,eatedQueue));
        exec.execute(new Eater(finishedQueue,eatedQueue));
        exec.execute(new Eater(finishedQueue,eatedQueue));
        TimeUnit.SECONDS.sleep(10);

        exec.shutdownNow();
        //最后统计三个队列中的信息
        print("=============================================");
        print(dryQueue.size());
        print(butteredQueue.size());
        print(finishedQueue.size());
        print(eatedQueue.size());
  }
}/*
Toast 0: DRY
Toast 0: DRY
Toast 0: DRY
Toast 1: DRY
Toast 1: DRY
Toast 1: DRY
Toast 2: DRY
Toast 2: DRY
Toast 2: DRY
Toast 3: DRY
...............
...............
Jammer interrupted
Toaster interrupted
Eater interrupted
Eater off
Toaster interrupted
Toaster off
Toaster off
Jammer off
Eater interrupted
Toaster interrupted
Toaster off
Eater off
Butterer interrupted
Butterer off
Eater interrupted
Eater off
=============================================
12733
1
675
297
/

最后附上github坐标
完整源码:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值