回顾:
阻塞队列,英文名叫BlockingQueue。首先他是一种队列,联系之前Java基础——集合中介绍的Queue与Collection,我们就很容易开始今天的阻塞队列的学习了。来看一下他们的接口定义:
Queue:
(方法很简单,就不一一注释解释其作用了。)
public interface Queue<E> extends Collection<E> { boolean add(E e); boolean offer(E e); E remove(); E poll(); E element(); E peek(); }
BlockingQueue:
因为是继承自Queue,方法基本类似,就不再写了。主要方法有三个:add,remove,element(用于检查)。
那到底什么是阻塞队列呢?联系前面java线程——模拟生产者与消费者的例子,在篮子中没有馒头时,生成者要放入馒头消费者才能食用;在篮子里装满馒头时,生产者就不能继续生产了,要等人去吃。阻塞队列就是生产者存放馒头的容器,消费者从中取出元素,也就是那个篮子。
public interface BlockingQueue<E> extends Queue<E> { ...... }
BlockingQueue的实现类有很多,这里介绍一个很简单的ArrayBlockingQueue。他是一个数据结构组成的阻塞队列,按照FIFO先进先出的方式排序。
继续来看他的定义,下面只列举了常用的两个方法:
public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable { //放入元素 public void put(E e) throws InterruptedException { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == items.length) notFull.await(); enqueue(e); } finally { lock.unlock(); } } //取元素 public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == 0) notEmpty.await(); return dequeue(); } finally { lock.unlock(); } } }
来个例子:
//Capacity为3的阻塞队列 final BlockingQueue queue = new ArrayBlockingQueue(3); for (int i = 0; i < 2; i++) { //1、放馒头线程 new Thread() { @Override public void run() { while (true) { try { Thread.sleep((long) (Math.random() * 10000)); System.out.println(Thread.currentThread().getName() + "准备放馒头!"); //每次放入一个馒头 queue.put(1); System.out.println(Thread.currentThread().getName() + "已经放了馒头," + "队列目前有" + queue.size() + "个馒头"); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } //2、取馒头线程 new Thread() { @Override public void run() { while (true) { try { Thread.sleep((long) (Math.random() * 10000)); System.out.println(Thread.currentThread().getName() + "准备取馒头!"); //拿走一个 queue.take(); System.out.println(Thread.currentThread().getName() + "已经取走馒头," + "队列目前有" + queue.size() + "个馒头"); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); }
部分执行结果:
对比:
和之前手动实现生产者消费者的例子相比,这次的实现就简单多了。不再需要去管篮子里的“取”和“放”的操作,不用关心线程的同步互斥问题,不用关心篮子是空了还是满了。只需要简单的调用put或take方法即可,把复杂的阻塞问题教给BlockingQueue去处理,但是原理还是一样的。