认识阻塞队列之前我们先来看看生产者——消费者模型。类比于包饺子,把包饺子分为两件事,擀面皮和包饺子。擀面皮就可以理解为生产者,包饺子可以理解为消费者.此时就会涉及到两种情况
- 生产者生产得快,消费者消费得慢。此时放饺子的那个交易所上面慢慢的就会放满饺子。这个时候就应该让生产者阻塞等待。
- 生产者生产得慢,消费者消费得快。此时放饺子的那个交易所上面慢慢的就会没有饺子。这个时候就应该让消费者阻塞等待。
阻塞队列就是用来实现生产者——消费者的。当我们入队列的时候发现队列已经满了,就应该让入队列操作阻塞等待;出队列的时候发现队列已经空了,就应该让出队列操作阻塞等待。
下面我们就来自己实现一个阻塞队列
public class ThreadDemo18 {//阻塞队列
static class BlockingQueue {
private int[] arr = new int[3];
private int head = 0;
private int tail = 0;
private int size = 0;
//阻塞队列只支持两个操作,入队列和出队列
public void put(int value) throws InterruptedException {
synchronized (this) {//不加锁的话并发执行的时候多个线程执行的事情都是一样的。
if(size==arr.length){//size等于数组长度的时候代表队列满了,就释放锁阻塞等候通知
this.wait();
}
arr[tail] = value;
tail++;
if(tail==arr.length){
tail = 0;
}
size++;
this.notify();//这里代表入队列成功了,发出消息表示可以出队列了此时
}
}
public int take() throws InterruptedException {
int ret;
synchronized (this) {
if(size==0){//这个代表队列空了,需要阻塞等候通知
this.wait();
}
ret = arr[head];
head++;
if(head==arr.length){
head=0;
}
size--;
this.notify();//当出队列操作成功之后代表此时队列有位置了,就发出通知
}
return ret;
}
}
public static void main(String[] args) {
BlockingQueue b = new BlockingQueue();
//搞两个线程,一个模拟生产者,也就是入队列操作。一个模拟消费者,也就是出队列操作
Thread t1 = new Thread() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
b.put(i);
System.out.println(("生产成功"+i));
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.start();
Thread t2 = new Thread(){
@Override
public void run() {
for (int j = 0; j < 5; j++) {
try {
int ret = b.take();
System.out.println(("消费成功" + ret));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t2.start();
}
}
阻塞队列和普通队列一样,也是先进先出的特性。只不过它所支持的操作就只有两个,入队列和出队列,没有取队首元素这个操作。通过wait和notify方法来实现阻塞操作。