Java中的阻塞队列
队列就是我们常说的先进先出的线性数据结构。
而阻塞队列是继承自队列的,相对于队列来说,多了两个重要的方法
- put方法 : put方法的作用是插入元素,通常在队列没有满的时候是正常插入。如果队列满了无法继续插入,这时它不会立刻返回false和抛出异常,而是让插入的线程进入阻塞状态等待,直到队列里面有空闲空间了。此时队列就会让之前的线程解除阻塞状态,并把刚才那个元素添加进去
- take方法 : take方法的作用是获取并移除队列的头节点。通常队列里面有元素会正常取出数据并移除;但是如果执行take的时候队列里无数据,则阻塞等待,直到队列里面有数据以后,就会解除阻塞状态,去获取数据。
Java中是有阻塞队列的实现的.
- BlockingQueue 是阻塞队列的接口
对于接口也有多种类型不同的实现
- LinkedBlockingQueue
- ArrayBlockingQueue
使用多线程基础 和 数组知识实现一个简单的阻塞队列.(完成读写操作)
package hello;
class MyBlockingQueue{
//保存数据本体
private int[] data = new int[1000];
//有效元素个数
private int size = 0;
//队首下标
private int head = 0;
//队尾下标
private int tail = 0;
//入队列
synchronized public void put(int value) throws InterruptedException {
if(size == data.length){
// System.out.println("队满");
// return;
this.wait();
}
//把新的元素放到tail的位置上
data[tail] = value;
tail++;
// tail %= data.length;
if(tail >= data.length){
tail = 0;
}
size++;
//有人等待,notify就会唤醒.没人等待,不会有任何副作用
this.notify();
}
//出队列
synchronized public Integer take() throws InterruptedException {
//判空
if(size == 0){
// return null;
this.wait();
}
//取出head位置的元素
int ret = data[head];
head++;
if(head >= data.length){
head = 0;
}
size--;
this.notify();
return ret;
}
}
public class Test {
private static MyBlockingQueue queue = new MyBlockingQueue();
public static void main(String[] args) {
Thread producer = new Thread(() -> {
int num = 0;
while(true){
try{
System.out.println("生产了: "+ num);
queue.put(num);
num++;
}catch (InterruptedException e) {
e.printStackTrace();
}
}
});
producer.start();
Thread customer = new Thread(()->{
while (true) {
try {
int num = queue.take();
System.out.println("消费了: "+num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
customer.start();
}
//生产者消费者模型测试
}
小bug
- 在实际运行中,我们的代码在判空和判满的时候有可能会被打断。
- 比如:很可能在别的代码里暗中 interrupt(),把wait()给提前唤醒了。 明明条件还没有满足(队列非空),但是wait唤醒之后就继续往下走了。所以更稳妥的做法是,我们在wait之后再进行判断一次。(使用while多次判断)
- 防止出现内存可见性优化。给计数变量添加 volatile 关键字
class MyBlockingQueue{
//保存数据本体
private int[] data = new int[1000];
//有效元素个数
volatile private int size = 0;
//队首下标
volatile private int head = 0;
//队尾下标
volatile private int tail = 0;
//入队列
synchronized public void put(int value) throws InterruptedException {
//判满 将 if 改为 while
while (size == data.length){
this.wait();
}
//把新的元素放到tail的位置上
data[tail] = value;
tail++;
if(tail >= data.length){
tail = 0;
}
size++;
//有人等待,notify就会唤醒.没人等待,不会有任何副作用
this.notify();
}
//出队列
synchronized public Integer take() throws InterruptedException {
//判空 将 if 改为 while
while (size == 0){
this.wait();
}
//取出head位置的元素
int ret = data[head];
head++;
if(head >= data.length){
head = 0;
}
size--;
this.notify();
return ret;
}
}
public class Test {
private static MyBlockingQueue queue = new MyBlockingQueue();
public static void main(String[] args) {
Thread producer = new Thread(() -> {
int num = 0;
while(true){
try{
System.out.println("生产了: "+ num);
queue.put(num);
num++;
}catch (InterruptedException e) {
e.printStackTrace();
}
}
});
producer.start();
Thread customer = new Thread(()->{
while (true) {
try {
int num = queue.take();
System.out.println("消费了: "+num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
customer.start();
}
//生产者消费者模型测试
}