阻塞队列
定义:顾名思义,首先它是一个队列,而一个阻塞队列在数据结构中所起的作用大致如下
当阻塞队列是空时,从队列中获取元素的操作会被阻塞。
当阻塞队列是满时,往队列里添加元素的操作将会被阻塞。
这个是消息队列的底层原理
优点:可以不用管阻塞或者唤醒,阻塞队列会解决,可以很简单的写出一个生产者消费者模式。
java实现类架构梳理
BlockingQueue是一个接口类,具体的实现类有以下7中:
ArrayBlockingQueue; 由数组结构组成的有界阻塞队列
LinkedBlockingDeque;由链表结构组成的有界(但大小默认值为Integer.MAX_VALUE)阻塞队列
PriorityBlockingQueue;支持优先级排序的无界阻塞队列
DelayQueue;使用优先级队列实现的延迟无阻塞队列
SynchronousQueue;不存储元素的阻塞队列,也即单个元素的队列
LinkedTransferQueue;由链表结构组成的无界阻塞队列。
LinkedBlocking==Deque ==;由链表结构组成的双向阻塞队列
具体使用方法
方法类型 | 抛出异常 | 特殊值 | 阻塞 | 超时 |
---|---|---|---|---|
插入 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
移除 | remove() | poll() | take() | poll(time,unit) |
检查 | element | peek | 不可用 | 不可用 |
1、就是抛异常的方法在插入满了之后,会报一个异常,remove一样,element是检查队头的元素或者是否为空。
2、特殊值的方法是在插入满之后返回值变成了false而不是一个异常,取出失败的时候返回null。
3、阻塞方法是在插入满之后把这个方法阻塞,一直等待队列空出来一个之后再进行加入,会出现一直等待,也可能出现饥饿现象。
4、超时方法的话,当阻塞队列满时,队列会阻塞生产者线程一定时间,超过限时后生产者线程会退出。
什么情况下应用?
- 生产者消费者模式
传统版的代码:
class ShareData{ //资源类
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment()throws Exception{
lock.lock();
try{
//1 判断
while (number!=0){
//等待不能生产
condition.await();
}
//干活
number++;
System.out.println(Thread.currentThread().getName()+"\t "+number);
//通知唤醒
condition.signalAll();
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void decrement()throws Exception{
lock.lock();
try{
//1 判断
while (number==0){
//等待不能生产
condition.await();
}
//干活
number--;
System.out.println(Thread.currentThread().getName()+"\t "+number);
//通知唤醒
condition.signalAll();
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
/**
* @author shihangqi
* @date 2019/10/25 - 14:42
*
* 题目:一个初始值为零的变量,两个线程对交替操作,一个加1一个减1,来5轮
*
* 1 线程 操作 资源类
* 2 判断 干活 通知
* 3 防止虚假唤醒 (在多线程中尽量用while不用if)虚假唤醒主要是因为
*
*/
public class ProdConsumer_TraditionDemo {
public static void main(String[] args) {
ShareData shareData = new ShareData();
new Thread(()->{
for(int i = 1; i <=5; i++){
try {
shareData.increment();
} catch (Exception e) {
e.printStackTrace();
}
}
},"aa").start();
new Thread(()->{
for(int i = 1; i <=5; i++){
try {
shareData.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
},"bb").start();
}
}
阻塞队列版的代码:
/**
* @author shihangqi
* @date 2019/11/5 - 10:05
*/
class MyResource{
private volatile boolean FLAG = true;
private AtomicInteger atomicInteger = new AtomicInteger();
BlockingQueue<String> blockingQueue = null;
public MyResource(BlockingQueue<String> blockingQueue) {
this.blockingQueue = blockingQueue;
System.out.println(blockingQueue.getClass().getName());
}
public void myProd()throws Exception{
String data = null;
boolean retValue;
while (FLAG){
data = atomicInteger.incrementAndGet()+"";
retValue = blockingQueue.offer(data,2, TimeUnit.SECONDS);
if (retValue){
System.out.println(Thread.currentThread().getName()+"\t 插入队列"+data+"成功");
}else {
System.out.println(Thread.currentThread().getName()+"\t 插入队列"+data+"失败");
}
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e){ e.printStackTrace(); }
}
System.out.println(Thread.currentThread().getName()+"\t 大老板叫停了,FLAG=false了");
}
public void myConsumer()throws Exception{
String result = null;
while (FLAG){
result = blockingQueue.poll(2,TimeUnit.SECONDS);
if (null == result || result.equalsIgnoreCase("")){
FLAG = false;
System.out.println(Thread.currentThread().getName()+"\t 超过2秒钟没有取到蛋糕,消费退出");
return;
}
System.out.println(Thread.currentThread().getName()+"\t 消费队列"+result+"成功");
}
}
public void stop(){
this.FLAG = false;
}
}
public class ProdConsumer_BlockQueueDemo {
public static void main(String[] args) {
MyResource myResource = new MyResource(new ArrayBlockingQueue<>(10));
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t 生产线程启动");
try {
myResource.myProd();
} catch (Exception e) {
e.printStackTrace();
}
},"Prod").start();
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t 消费线程启动");
try {
myResource.myConsumer();
} catch (Exception e) {
e.printStackTrace();
}
},"Consumer").start();
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e){ e.printStackTrace(); }
System.out.println("5秒过了叫停");
myResource.stop();
}
}