一、BlockingQueue阻塞先入先出队列
BlockingQueue阻塞先入先出队列,通常用于生产者和消费者模型中。因为BlockingQueue接口的实现类是线程安全的,所以可以被多个生产者线程和多个消费者线程共享同一个队列对象实例,生产者负责往队列中填充数据,消费者负责从队列中读取数据。
在BlockingQueue的接口设计层面,添加和读取数据的操作主要分为以下两种。
(1) 从队列尾部添加数据元素
(2) 从队列头部获取并删除数据元素
二、用法
基于读写操作失败时的处理方式的不同,BlockingQueue接口提供了多个版而的增删方法实现,主要基于阻塞和非阻塞来分类,核心方法如下。
1.数据添加到队列尾:如果队列满了,没有空闲空间,无法添加数据,则按以下方法处理。
(1) add方法:抛异常,需要应用代码捕获这个异常
(2) put方法:无限阻塞等待,直到队列不满,有空闲空间。
(3) offer方法:非阻塞,如果队列满了,直接返回false,则表示添加失败
(4) offer timeout超时版本方法:阻塞等待指定的时间,如果超时后还是无法添加成功,则自动退出阻塞。
2.从队列头读取数据,如果队列为空,则按以下方法处理。
(1) element方法,抛异常,其中element方法只是读取队列头的数据,不会删除队列头的数据
(2) take方法:无限阻塞等待,直到队列存在数据可读。
(3) poll方法:非阻塞等待,当队列为空,没有数据可读时,直接返回空值null。
(4) peek方法:非阻塞读取,与element一样,也只是读取队列头数据,不会删除队列头的数据。
(5) poll timeout:超时版本方法,阻塞等待指定时间,如果超时还没有读到数据,则退出阻塞。
3.从队列头删除数据,如果队列为空,则按以下方法处理。
(1) remove方法:非阻塞,直接返回false.
(2) poll方法:非阻塞,直接返回false.
(3) take方法:无限阻塞等待,直到队列存在数据可删。
(4) poll timeout超时版本方法:阻塞等待指定的时间,如果超时还没有数据,则自动退出阻塞。
除以上针对操作失败情况的不同处理方法外,BlockingQueue还包括以下特性:
1.空值null:在add、put、offer方法中,均不能添加空值null,否则抛空指针异常
2.容量:如果在创建队列的对象实例时不指定最大容量,则默认最大容量为Intger.MAX_VALUE
3.线程安全:BlockingQueue是线程安全的,遵循内存可见性的happend-before原则,即往队列写入数据的线程优于从队列读取或删除数据的线程,从而保证一个线程的写对其他线程可见。
三、demo案例
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 先入先出队列BlockingQueue
*/
public class BlockingQueueDemo {
public static void main(String[] args) {
BlockingQueue<String> q = new LinkedBlockingQueue<>();
//一个生产者
Producer p = new Producer(q);
//两个消费者
Consumer consumer1 = new Consumer(q);
Consumer consumer2 = new Consumer(q);
new Thread(p).start();
new Thread(consumer1).start();
new Thread(consumer2).start();
}
private static AtomicInteger seq = new AtomicInteger(0);
/**
* 生产者
*/
static class Producer implements Runnable{
private final BlockingQueue<String> queue;
//将共享的队列通过构造方法传入生产者
Producer(BlockingQueue<String> queue){
this.queue = queue;
}
public void run(){
//持续往队列中生产产品,如果队列已满,则阻塞等待。
try {
while(true){
queue.put(produce());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产产品
String produce(){
return "产品-"+seq.incrementAndGet();
}
}
static class Consumer implements Runnable{
private final BlockingQueue<String> queue;
//将共享的队列通过构造方法传入消费者
Consumer(BlockingQueue<String> queue){
this.queue = queue;
}
public void run(){
//消费队列中的产品,如果队列为空,则阻塞等待
try {
while(true){
consume(queue.take());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
void consume(String product){
System.out.println(Thread.currentThread().getName()+"-消费产品:"+product);
}
}
}