阻塞队列BlockingQueue
一、阻塞队列介绍
阻塞队列(BlockingQueue)是一个接口,位于java.util.concurrent
包下
-
该接口实现了
Collection
、Iterable
、Queue
接口 -
常用两个实现类:
ArrayBlockingQueue
、SynchronousQueue
-
队列:该数据结构就不细讲了
二、为什么需要阻塞队列
首先我们需要回到传统设计的生产者消费者模式
- 在这种模式中,有一种情况就是,当生产者生产的数据为空时,那么消费者就需要进入等待,并通知消费者进行处理
- 那如果生产者产生到指定数据,它也要进入等待,再取通知消费者处理
- 当中这一系列操作都需要我们去手动编写,还要兼顾效率和线程安全问题。
所以就出现了阻塞队列
- 阻塞队列的出现就是用于解决传统的生产者消费者模式
- 生产的数据直接放进该队列
- 基本情况是这样,通过队列容量来判断
- 当队列为空时,·那么消费者线程进入等待
- 队列数据到限定容量时,生产者进入等待
既然为一个封装好的数据结构,那么我们就要学会他的各种API使用方式和注意点
三、API介绍
添加数据 | 抛出异常 | 返回值 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
add | yes(队列异常) | 布尔 | no | no |
offer | no | 布尔 | no | yes【重载方法】 |
put | yes(中断) | void | yes | no |
删除数据 | 抛出异常 | 返回值 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
remove | yes(队列异常) | 数据 | no | no |
pool | no | 数据(队列空返回null) | no | yes【重载方法】 |
take | yes(中断) | 数据 | yes | no |
虽然有很多方法但是对于阻塞队列(比如ArrayBlockingQueue
)就只使用put
和take
方法
- 只有当队列满才会去等待
对于SynchronousQueue
实现类,注意点就是:这是一个同步队列
- 大概意思就是:不论再多的数据要生产,但队列中只会存在一条数据
- 生产者产生一条数据,就会立即等待消费
- 也依然只使用以下两个方法
-
put
、take
-
在测试下,使用其他方法会出现错误
-
四、实现生产、消费者模式
package com.migu.procon;
import jdk.nashorn.internal.ir.CallNode;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
public class ProducerAndConsumer3 {
public static void main(String[] args) {
Data3 data = new Data3();
// 生产者线程
new Thread(() -> {
while (true)
data.increment();
}).start();
// 消费者线程
new Thread(() -> {
while (true)
data.decrement();
}).start();
}
}
// 数据资源
class Data3 {
// 两个线程分别对象两个模块,就不设置锁了
int count = 0;
ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue(1);
// 生产模块
public void increment() {
try {
queue.put(10);
System.out.println("生产后,当前数据量为: " + (++count));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 消费模块
public void decrement() {
try {
TimeUnit.SECONDS.sleep(1);
queue.take();
System.out.println("消费后,当前数据量为: " + (--count));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出:
以上方案就当于同步队列SynchronousQueue
,因为自始至终只有一份数据