BlockingQueue接口是Queue的子接口,但它的主要用途不是作为容器,而是作为线程同步的工具。BlockingQueue具有一个特征:当生产者线程试图向BlockingQueue中放入元素时,如果该队列已满,则该线程被阻塞;当消费者线程试图从BlockingQueue中取出元素时,如果该队列已空,则该线程被阻塞。BlockingQueue提供如下两个支持阻塞的方法:
- void put(E e) :将指定元素插入此队列中,将等待可用的空间
- E take() :获取并移除此队列的头部,在元素变得可用之前一直等待
BlockingQueue有5个具体的实现类,根据不同需求,选择不同的实现类:
- ArrayBlockingQueue:规定大小的BlockingQueue,基于数组实现,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入先出)顺序排序的;
- LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定.其所含的对象是以FIFO(先入先出)顺序排序的;
- PriorityBlockingQueue:类似于LinkedBlockQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序;当取出队头元素时,是取出队列中最小的元素。
- SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的;
- DelayQueue:要求集合元素都实现Delay接口,(该接口中只有一个long getDelay(TimeUnit unit):返回与此对象相关的剩余延迟时间,以给定的时间单位表示)。但Delay接口实现了Comparable接口,因此还需实现int compareTo()方法。DelayQueue根据集合元素的getDelay()方法的返回值进行排序,该队列的头部 是延迟期满后保存时间最长的 Delayed 元素,当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于等于 0 的值时,将发生到期。只有在延迟期满时才能从中提取元素,每次取出的队头元素是到期后存在时间最久的元素。
下面是一个阻塞队列的用法:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
class Bread implements Comparable<Bread>{
String cooker;
long makeTime;
public Bread(String cooker,long makeTime) {
this.cooker = cooker;
this.makeTime = makeTime;
}
@Override
public int compareTo(Bread bread) {
if(makeTime>bread.makeTime) return 1;
else if(makeTime<bread.makeTime) return -1;
else return 0;
}
}
class Producer implements Runnable{
private BlockingQueue<Bread> q;
String cooker;
private long makeTime;
public Producer(BlockingQueue<Bread> q,String cooker) {
this.q = q;
this.cooker = cooker;
}
@Override
public void run() {
for(int i=0;i<3;i++){
Bread bread = new Bread(cooker,makeTime=System.nanoTime());
try {
q.put(bread);
System.out.println(cooker+" makes one piece of bread at "+makeTime);
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable{
private BlockingQueue<Bread> q;
String consumer;
public Consumer(BlockingQueue<Bread> q,String consumer) {
this.q = q;
this.consumer = consumer;
}
@Override
public void run() {
for(int i=0;i<2;i++){
try {
Bread bread = q.take();
System.out.println(consumer+" buys the bread made by "+bread.cooker
+" at "+System.nanoTime());
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class BlockingQueueTest {
public static void main(String[] args) {
BlockingQueue<Bread> queue = new PriorityBlockingQueue<Bread>(2);
for(int i=0;i<2;i++){
new Thread(new Producer(queue, "cooker"+i)).start();
}
for(int i=0;i<3;i++){
new Thread(new Consumer(queue, "consumer"+i)).start();
}
//System.out.println("Remaining "+queue.size()+" pieces of bread");
}
}