目录
标准库中的阻塞队列
标准库中为我们提供了BlockingQueue这个类,我们可以调用这个类中的put,take方法来分别插入元素和获取元素。和普通队列相同的是都是遵循先进先出的原则,不同的是阻塞队列多了阻塞的功能,当队列为空的时候我们继续take取元素就会阻塞,同样当队列满的时候我们继续put也会阻塞。
可以看出我们进行了两次take,而队列里只有一个元素,所以打印了一个“1”之后,线程就阻塞了。
实现阻塞队列
普通队列
我们自己实现带有阻塞功能的队列,首先我们先实现一个普通的队列。(这里主要实现put和take两个主要的方法)
我们采用循环数组的方式来实现队列。head表示队列的头下标,tail表示队列的为下标。循环队列,我们当然要使下标到达数组长度的时候循环下标。这里直接将下标置为0即可。
代码
public class MyBlockingQueue {
//采用循环队列的方式来实现阻塞队列
private int[] elem = new int[1000];
private int head;//头
private int tail;//尾
private int size;//更新当前队列中的有效数据的个数
//put方法,来往队列里存数据
public void put(int numb){
if(size == elem.length){
//队列满了
System.out.println("队列已满");
return;
}
//新数据添加到tail位置
elem[tail] = numb;
tail++;
if(tail == elem.length){
//让下标进行循环
tail = 0;
}
size++;
}
//take方法,从队列中取出元素
public int take(){
if(size == 0){
//当前队列中没有元素
return -1;
}
//取出head下标的元素
int ret = elem[head];
head++;
if(head == elem.length){
//同理,循环下标
head = 0;
}
size--;
return ret;
}
}
线程安全问题
在多线程环境下,当前的代码是有线程安全问题的,这里回顾一下线程安全问题的原因
对应我们目前的代码,head,tail,size,elem数组,这些变量都存在多个线程修改的问题。所以这里简单处理,可使用volatile来修饰变量,给put和take方法上加锁。
修改后的代码
public class MyBlockingQueue {
//采用循环队列的方式来实现阻塞队列
volatile private int[] elem = new int[1000];
volatile private int head;//头
volatile private int tail;//尾
volatile private int size;//更新当前队列中的有效数据的个数
//put方法,来往队列里存数据
synchronized public void put(int numb){
if(size == elem.length){
//队列满了
System.out.println("队列已满");
return;
}
//新数据添加到tail位置
elem[tail] = numb;
tail++;
if(tail == elem.length){
//让下标进行循环
tail = 0;
}
size++;
}
//take方法,从队列中取出元素
synchronized public int take(){
if(size == 0){
//当前队列中没有元素
return -1;
}
//取出head下标的元素
int ret = elem[head];
head++;
if(head == elem.length){
//同理,循环下标
head = 0;
}
size--;
return ret;
}
}
实现阻塞功能
我们需要的阻塞效果是:1.当队列为空时再take会阻塞。2.当队列为满时再put会阻塞。
而当前我们的代码再这两个地方只是返回一个错误。并没有阻塞。前面学习我们也知道,wait有阻塞功能,当然wait的用法也需要搭配notify和锁。
代码
public class MyBlockingQueue {
//定义一个锁对象
Object clock = new Object();
//采用循环队列的方式来实现阻塞队列
volatile private int[] elem = new int[1000];
volatile private int head;//头
volatile private int tail;//尾
volatile private int size;//更新当前队列中的有效数据的个数
//put方法,来往队列里存数据
synchronized public void put(int numb) throws InterruptedException {
if(size == elem.length){
//队列满了
// System.out.println("队列已满");
// return;
clock.wait();
}
//新数据添加到tail位置
elem[tail] = numb;
tail++;
if(tail == elem.length){
//让下标进行循环
tail = 0;
}
size++;
clock.notify();
}
//take方法,从队列中取出元素
synchronized public int take() throws InterruptedException {
if(size == 0){
//当前队列中没有元素
//return -1;
clock.wait();
}
//取出head下标的元素
int ret = elem[head];
head++;
if(head == elem.length){
//同理,循环下标
head = 0;
}
size--;
clock.notify();
return ret;
}
}