一、什么是阻塞队列
阻塞队列,可以理解成生产者消费者模式,是一个支持两个附加操作的队列。
- 当队列为满的时候,插入元素的线程被阻塞,直达队列不满。
- 当队列为空的时候,获取元素的线程被阻塞,直到队列不空。
生产者消费者模式:
生产者——生产数据的线程,消费者——消费数据的线程。
在多线程开发中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。反之,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这种生产消费能力不均衡的问题,便有了生产者和消费者模式。生产者和消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通信,而是通过阻塞队列来进行通信,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
二、常用方法
抛出异常 | 返回值 | 阻塞 | 超时 | |
---|---|---|---|---|
插入 | add(Object obj) | offer(Objject obj) | put(Object obj) | offer(Object obj,int time,unit) |
移除 | remove() | pull() | take() | pull(time,unit) |
检查 | element() | peek() | 不可用 | 不可用 |
- 抛出异常:当队列满时,如果再往队列里插入元素,会抛出IllegalStateException(“Queuefull”) 异常。当队列空时,从队列 里获取元素会抛出NoSuchElementException异常。
- 返回特殊值:当往队列里插入元素时,成功返回true,失败返回false。如果是移除方法,则从队列里取出一个元素,如果没有则返回null。
- 一直阻塞:当阻塞队列为满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到队列可用或者响应中断退出。当队列为空时,如果消费者线程从队列里移除(take)元素,队列会阻塞住消费者线程,直到队列不为空。
- 超时退出:当阻塞队列满时,如果生产者线程往队列里插入元素,队列会阻塞生产者线程一段时间,如果超过了指定的时间,生产者线程会自动退出。
三、常用阻塞队列
- ArrayBlockingQueue:由数组结构组成的有界阻塞队列,按照先进先出原则,要求设定初始大小。
- LinkedBlockingQueue:由链表结构组成的有界,按照先进先出原则,可以不设定初始大小,但是默认值大小为Integer,MAX_VALUE的阻塞队列。
- PriorityBlockingQueue:支持优先级排序的无界阻塞队列。默认情况下,按照自然顺序,要么实现compareTo()方法,指
定构造参数Comparator。 - DelayQueue:一个使用优先级队列实现的无界阻塞队列,支持延时获取元素的阻塞队列,元素必须要实现Delayed接口。适用场景:实现自己的缓存系统,订单到期,限时支付等等。
- SynchronousQueue:一个不存储元素的阻塞队列,没有容量,每一个put操作都要等待一个take操作,所以会产生生产者和消费者能力不匹
配的问题。 - LinkedTransferQueue:由链表结构组成的无界阻塞队列。transfer(),必须要消费者消费了以后方法才会返回,tryTransfer()
无论消费者是否接收,方法都立即返回。 - LinkedBlockingDeque:由链表结构组成的双向阻塞队列。可以从队列的头和尾插入和移除元素,实现工作密取,方法名带了First对头部操作,带了last从尾部操作,另外:add=addLast;
remove=removeFirst; take=takeFirst。
四、使用示例
生产者消费者案例:
生产者(Producer):
package com.zm.producer;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
//生产者线程
public class Producer implements Runnable {
private BlockingQueue<Integer> blockingQueue ; //阻塞队列
private volatile boolean flag; //是否阻塞的标志
private Random random; //生产的元素
public Producer(BlockingQueue<Integer> blockingQueue) {
this.blockingQueue = blockingQueue;
flag = false;
random = new Random();
}
@Override
public void run() {
if(!flag){
int rnum = random.nextInt(10);
try {
//生产者往队列里放元素
blockingQueue.put(rnum);
System.out.println(Thread.currentThread().getName()+"producer:"+rnum);
//休眠1秒钟
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//阻塞退出
public void shutdown(){
flag = true;
}
}
消费者(Customer):
package com.zm.customer;
import java.util.concurrent.BlockingQueue;
//消费者线程
public class Customer implements Runnable {
private BlockingQueue<Integer> blockingQueue; //阻塞队列
private volatile boolean flag; //阻塞标志
public Customer(BlockingQueue<Integer> blockingQueue) {
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
if(!flag){
int rnum;
try {
//消费者从阻塞队列中取元素,并返回去取出来的这个元素
rnum = blockingQueue.take();
System.out.println(Thread.currentThread().getName()+"customer:"+rnum);
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//阻塞退出
public void shutdown(){
flag = true;
}
}
测试(BlockingQueenTest ):
package com.zm.test;
import com.zm.customer.Customer;
import com.zm.producer.Producer;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class BlockingQueenTest {
public static void main(String[] args) throws InterruptedException {
//创建一个由链表组成的双向阻塞队列
BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<>();
Producer producer = new Producer(blockingQueue);
Customer customer = new Customer(blockingQueue);
//创建5个生产者,5个消费者
for(int i=0;i<10;i++){
if(i<5){
new Thread(producer,"生产者: "+i).start(); //将生产者对象放入线程中
}else{
new Thread(customer,"消费者: "+(i-5)).start(); //将消费者对象放入线程中
}
}
Thread.sleep(1000);
//阻塞退出
producer.shutdown();
customer.shutdown();
}
}