目录
1.什么是阻塞队列
阻塞队列和普通的队列的区别是:
当阻塞队列是空的,从队列中获取元素的操作将会被阻塞;
当阻塞队列是满的,往队列里添加元素的操作将会被阻塞;
试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素;
同样,试图从满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他线程从列中移除一个或者多个元素或者完全清空队列使队列重新变得空闲起来并后续新增。
在多线程领域里:所谓阻塞,在某些情况下会挂起线程,一旦条件满足,被挂起的线程又会自动被唤醒。
2.有哪些常见的阻塞队列(加粗即常用的)
- ArrayBlockingQueue:由数组结构组成的有界阻塞队列
- LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为Integer.MAX_VALUE)阻塞队列
- SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列,只存一个元素
- PriorityBlockingQueue:支持优先级排序的无界阻塞队列
- DelayBlockingQueue:使用优先级队列实现的延迟无界阻塞队列
- LinkedTransferQueue:由链表结构组成的无界阻塞队列
- LinkedBlockingDeque:由链表结构组成的双向阻塞队列
SynchronousQueue没有容量。与其他BlockingQueue不同,SynchronousQueue是不存储元素,每一个put操作必须要等待一个take操作,否则不能继续添加元素,反之亦然。
我们为什么要使用阻塞队列呢?
尽量让我们的程序做到高内聚,低耦合
3.实现简单的阻塞队列
//实现阻塞队列
public class MyBlockingQueue {
//需要一个数据来保存数据
private Integer[] elementData=new Integer[500];
//队首与队尾的下标
private volatile int head=0;
private volatile int tail=0;
//有效元素的个数
private volatile int size=0;
public void put(Integer value) throws InterruptedException {
synchronized (this) {
//先判断队列是否满了
while (size >= elementData.length) {
this.wait();
}
//队尾入队
elementData[tail] = value;
tail++;
if (tail>=elementData.length){
tail=0;
}
size++;
//出队后去唤醒其他线程
this.notifyAll();
}
}
public Integer take() throws InterruptedException {
synchronized (this) {
//先判断队列是否为空
while (size == 0) {
this.wait();
}
//出队队首元素
Integer value = elementData[head];
head++;
if (head>= elementData.length){
head=0;
}
size--;
this.notifyAll();
return value;
}
}
}
我们在put操作和take操作的最后都加入了notify方法,就是在元素入队出队时可以及时唤醒
1.在普通队列的基础上加入了等待操作,在入队时如果队列已满就需要等待,在出队时如果队列为空就需要等待
2.在普通队列的基础上加入了唤醒操作,执行完入队操作后唤醒出队线程,执行完出队操作后唤醒入队线程。
4.生产者消费者模型
import java.util.concurrent.TimeUnit;
//用两个模型实现生产者消费者模型
public class Demo_BlockingQueue03 {
public static void main(String[] args) {
MyBlockingQueue queue=new MyBlockingQueue();
//创建生产者线程
Thread producer=new Thread(()->{
int sum=0;
while (true){
try {
//生产好的元素加入队列
queue.put(sum);
System.out.println("生产的元素:"+sum);
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
sum++;
}
});
producer.start();
//创建消费者线程
Thread consumer=new Thread(()->{
while (true){
try {
//取出元素,表示消费过程
Integer result=queue.take();
System.out.println("消费的元素:"+result);
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
consumer.start();
}
}
我们的生产者模型生产的快,消费者模型消费的慢,具体可以看代码执行
可以看出的现象是,因为生产者速度快,当生产者将队列添加满后,开始阻塞,等消费者一点一点开始消费,生产者跟着再一点一点生产。