1、介绍
阻塞队列,其实就是满足队列操作(先进先出)的一个数据集合,同时满足存取数据时的阻塞功能。
- 先进入阻塞队列的对象,会先被消费掉。
- 当阻塞队列已满时,生产者线程添加对象操作会被阻塞。
- 当阻塞队列已空时,消费者线程取出对象操作会被阻塞。
2、API分析
阻塞队列的直接接口为BlockingQueue,此接口内部定义了操作队列的各种方法。
BlockingQueue间接实现了Collection接口,所以阻塞队列其实就是一个集合,操作集合的各种方法,阻塞队列也满足。
核心方法
方法类型 | 抛出异常 | 阻塞 | 特殊值 | 阻塞超时 |
---|---|---|---|---|
添加 | boolean add(E e); | void put(E e); | boolean offer(E e); | boolean offer(E e, long timeout, TimeUnit unit); |
移除 | E remove(); | E take(); | E poll(); | E poll(long timeout, TimeUnit unit); |
检查 | E element(); | ✖ | E peek(); | ✖ |
核心方法详解
boolean add(E e):往队列尾部添加一个元素,成功返回true,队列已满抛出异常。
void put(E e):往队列尾部添加一个元素,队列已满时操作线程阻塞,直到队列不满。
boolean offer(E e):往队列尾部添加一个元素,成功返回true,失败返回false。
boolean offer(E e, long timeout, TimeUnit unit):往队列尾部添加一个元素,成功返回true,队列已满时,线程阻塞等待指定时长后,若队列仍为满,失败返回false。
E remove():返回并移除队列头部的第一个元素,队列已空抛出异常。
E take():返回并移除队列头部的第一个元素,队列已空时操作线程阻塞,直到队列非空。
E poll():返回并移除队列头部的第一个元素,队列已空时返回null。
E poll(long timeout, TimeUnit unit):返回并移除队列头部的第一个元素,队列已空时,线程阻塞等待指定时长后,若队列仍为空,返回null。
E element():仅返回队列头部第一个元素,队列中元素个数不变,队列已空抛出异常。
E peek():仅返回队列头部第一个元素,队列中元素个数不变,队列已空时返回null。
3、常见的阻塞队列
JDK8中BlockingQueue的实现类:
ArrayBlockingQueue:由数组结构组成的有界阻塞队列(必须指定大小)。
LinkedBlockingQueue:由链表结构组成的有界阻塞队列(默认大小Integer.MAX_VALUE,可视为无限大)。
SynchronousQueue:不存储元素的阻塞队列。
DelayQueue:基于PriorityQueue实现的支持延时获取元素的阻塞队列。
PriorityBlockingQueue:支持优先级排序的无界阻塞队列。
DelayedWorkQueue:定制的优先级队列,只能用来存储RunnableScheduledFutures任务。
LinkedTransferQueue:由链表结构组成的无界阻塞队列。
4、演示使用
演示抛出异常的操作
/**
* 1、添加元素操作
*/
public class Demo {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> queue = new ArrayBlockingQueue<String>(3);
queue.add("张三");
queue.add("李四");
queue.add("王五");
System.out.println(queue);
queue.add("赵六");
}
}
/**
* 运行结果,添加第四个元素的时候队列已满,报错
*/
[张三, 李四, 王五]
Exception in thread "main" java.lang.IllegalStateException: Queue full
/**
* 2、取元素操作
*/
public class Demo {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> queue = new ArrayBlockingQueue<String>(3);
queue.add("张三");
queue.add("李四");
System.out.println(queue);
System.out.println(queue.remove());
System.out.println(queue.remove());
//队列里面仅有两个元素,此时队列已空,再调用remove()会报错
System.out.println(queue.remove());
}
}
/**
* 运行结果,队列已空时,再调用remove()会报错
*/
[张三, 李四]
张三
李四
Exception in thread "main" java.util.NoSuchElementException
/**
* 3、检查元素操作
*/
public class Demo {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> queue = new ArrayBlockingQueue<String>(3);
queue.add("张三");
System.out.println(queue.element());
queue.remove();
//此时队列已空,调用element()会抛出异常
System.out.println(queue.element());
}
}
/**
* 运行结果,队列已空时,再调用element()会抛出异常
*/
张三
Exception in thread "main" java.util.NoSuchElementException
演示阻塞的操作
public class Demo {
static BlockingQueue<String> queue = new ArrayBlockingQueue<String>(3);
static DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) throws InterruptedException {
new Thread(new Producer(queue)).start();
new Thread(new Consumer(queue)).start();
//queue.take();
}
}
/**
* 生产者线程
*/
class Producer implements Runnable{
BlockingQueue<String> queue = null;
public Producer(BlockingQueue<String> queue){
this.queue = queue;
}
@Override
public void run() {
//每秒钟生产一个元素进队列
for(int i=1;i<=6;i++){
try {
TimeUnit.SECONDS.sleep(1);
String msg = "元素"+i;
queue.put(msg);
System.out.println(LocalDateTime.now().format(Demo.format)+" 生产者已放入 "+msg+" 进入队列");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 消费者线程
*/
class Consumer implements Runnable{
BlockingQueue<String> queue = null;
public Consumer(BlockingQueue<String> queue){
this.queue = queue;
}
@Override
public void run() {
//每三秒钟从队列消费一个元素
while (true){
try {
TimeUnit.SECONDS.sleep(3);
//队列元素取完后,若生产者线程不放入元素,此处将一直阻塞
String msg = queue.take();
System.out.println(LocalDateTime.now().format(Demo.format)+" 消费者已从队列中取到消息 "+msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 运行结果,当队列已满时,生产者线程被阻塞,当队列已空时,消费者线程被阻塞
*/
2022-04-17 13:37:18 生产者已放入 元素1 进入队列
2022-04-17 13:37:19 生产者已放入 元素2 进入队列
2022-04-17 13:37:20 消费者已从队列中取到消息 元素1
2022-04-17 13:37:20 生产者已放入 元素3 进入队列
2022-04-17 13:37:21 生产者已放入 元素4 进入队列
2022-04-17 13:37:23 消费者已从队列中取到消息 元素2
2022-04-17 13:37:23 生产者已放入 元素5 进入队列
2022-04-17 13:37:26 消费者已从队列中取到消息 元素3
2022-04-17 13:37:26 生产者已放入 元素6 进入队列
2022-04-17 13:37:29 消费者已从队列中取到消息 元素4
2022-04-17 13:37:32 消费者已从队列中取到消息 元素5
2022-04-17 13:37:35 消费者已从队列中取到消息 元素6