德华:嫂子,为什么会有阻塞队列呢?
安杰:它的产生主要有两个原因,一个是“不得不”,另一个是“应该有”。
不得不:就像我们去银行排队取钱,当没有提供给你的窗口的时候,我们就需要坐在小板凳上,看着《父母爱情》。
应该有:比如说经营一家餐厅,我们当然希望自己准备的餐桌永远都不空,当没有餐桌的时候,就让顾客先等一会儿。
下面是详细介绍
那么它的出现对我们有什么好处呢?
我们不在需要自己去控制那些细节,BlockingQueue就能帮我们搞定它。
现在看它怎么用:
我们以ArrayBlockingQueue为例
首先大家结合下面的代码,大家来看看这个表
add,remove,element
BlockingQueue<String> blockingQueue =new ArrayBlockingQueue<>(3);
System.out.println( blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
System.out.println(blockingQueue.add("x"));
System.out.println(blockingQueue.element());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
结果:
true
true
true
Exception in thread "main" java.lang.IllegalStateException: Queue full
at java.base/java.util.AbstractQueue.add(AbstractQueue.java:98)
at java.base/java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:326)
这是一个add超限的实现,同样当remove超限也会报错。
System.out.println( blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
System.out.println(blockingQueue.offer("x"));
System.out.println(blockingQueue.peek());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
结果:
true
true
true
false
a
a
b
c
null
可以看出,offer和poll还有peek都是很有礼貌的方法,满或空的时候,都会告诉你你的操作成功与否,而不像add他们几个,直接给你抛出一个异常这么暴力。
下面是put和take
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BQDemo2 {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
blockingQueue.put("2");
blockingQueue.put("3");
blockingQueue.put("4");
System.out.println("----------------------");
blockingQueue.put("9");
System.out.println("----------------------");
blockingQueue.take();
blockingQueue.take();
blockingQueue.take();
}
}
结果
----------------------
并且此时运行并没有停止,所以是运行到了第四个put时main线程被阻塞了。
下面这个是另外offer和poll加了参数。
blockingQueue.offer("1",2, TimeUnit.SECONDS);
blockingQueue.offer("1",2, TimeUnit.SECONDS);
blockingQueue.offer("1",2, TimeUnit.SECONDS);
blockingQueue.offer("1",2, TimeUnit.SECONDS);
System.out.println("------=========--------");
blockingQueue.poll(2, TimeUnit.SECONDS);
blockingQueue.poll(2, TimeUnit.SECONDS);
blockingQueue.poll(2, TimeUnit.SECONDS);
blockingQueue.poll(2, TimeUnit.SECONDS);
System.out.println("=======-------========");
结果
------=========--------
=======-------========
总结一下:
add,remove,element都是直接率性的性格,你只要有错他们会给你一个重重的教训。
offer,poll 不设置参数的的时候是友好的,当你有错时,他们只会提醒你你出错了。
put,take 是死脑筋,你告诉它我要插入或取出时,当满或空时,它就会一直等你。
而设置了参数的offer,poll 就像是已经明智的小朋友,有自己的思考,有耐心,有底线,会先等你,如果超过他的底线了,才会告诉你false。
下面是一个生产者消费者的实现。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class PCDemo {
static int num =0;
Lock lock= new ReentrantLock();
public Condition condition=lock.newCondition();
public void increase() throws Exception {
lock.lock();
try {
while (num!=0){
System.out.println("等待消费"+num);
condition.await();
}
num++;
condition.signalAll();
System.out.println("生产一个,此时num="+num);
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void decrease() throws Exception {
lock.lock();
try {
while (num==0){
System.out.println("等待生产 "+num);
condition.await();
}
num--;
condition.signalAll();
System.out.println("消费一个,此时num="+num);
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public static void main(String[] args) {
PCDemo pcDemo =new PCDemo();
new Thread(()->{
try {
for (int i = 0;i<5;i++){
pcDemo.increase();
}
} catch (Exception e) {
e.printStackTrace();
}
},"AAA" ).start();
new Thread(()->{
try {
for (int i = 0;i<5;i++) {
pcDemo.decrease();
}
} catch (Exception e) {
e.printStackTrace();
}
},"BBB" ).start();
}
}
结果
生产一个,此时num=1
等待消费1
消费一个,此时num=0
等待生产 0
生产一个,此时num=1
等待消费1
消费一个,此时num=0
等待生产 0
生产一个,此时num=1
等待消费1
消费一个,此时num=0
等待生产 0
生产一个,此时num=1
等待消费1
消费一个,此时num=0
生产一个,此时num=1
消费一个,此时num=0