前言
在多线程领域,所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动呗唤醒。 为什么需要阻塞队列? 好处是我们不需要关心什么时候需要阻塞线程,什么手需要唤醒线程,因为这一切阻塞队列都一手包办了,在concurrent包发布之前,在多线程环境下,我们每个程序员都必须要自己控制这些细节,尤其还要兼顾效率和线程安全,而这给我们的程序带来不小的复杂度。
提示:以下是本篇文章正文内容,下面案例可供参考
一、阻塞队列的种类
BlockingQueue为阻塞队列的顶级接口,下边有几种阻塞队列:
ArrayBlockingQueue:由数组结构组成的有界阻塞队列。 LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为Integer.MAX_VALUE)阻塞队列。 PriorityBlockingQueue:支持优先级排序的无界阻塞队列。 DelayQueue:使用优先级队列实现的延迟无界阻塞队列。 SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列。 LinkedTransferQueue:由链表结构组成的无界阻塞队列。 LinkedBlockingDeque:由链表组成的双向阻塞队列。
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
首先看一下抛出异常的代码:
public class BlockQueueDemo {
public static void main(String[] args) {
// 创建阻塞队列,3为阻塞队列的容量
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a")); // true
System.out.println(blockingQueue.add("b")); // true
System.out.println(blockingQueue.add("c")); // true
// 打开此行 报错 Queue full
// System.out.println(blockingQueue.add("x")); // 队列已满,不能再添加
// 查看阻塞队列里边是否有元素,有的话拿出第一个元素 a, 没有的话报错 NoSuchElementException
System.out.println(blockingQueue.element()); // a
System.out.println(blockingQueue.remove()); // a
System.out.println(blockingQueue.remove()); // b
System.out.println(blockingQueue.remove()); // c
// 打开此行 报错 NoSuchElementException
// System.out.println(blockingQueue.remove()); // 已经没有元素,不能再移除
}
}
看一下特殊值的代码:
public class BlockQueueDemo {
public static void main(String[] args) {
// 创建阻塞队列,3为阻塞队列的容量
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a")); // true
System.out.println(blockingQueue.offer("b")); // true
System.out.println(blockingQueue.offer("c")); // true
// 打开此行 不会和add方法一样报错 Queue full
System.out.println(blockingQueue.offer("x")); // false
// 查看阻塞队列里边是否有元素,有的话拿出第一个元素 a, 没有的话报错 返回null
System.out.println(blockingQueue.peek()); // a
System.out.println(blockingQueue.poll()); // a
System.out.println(blockingQueue.poll()); // b
System.out.println(blockingQueue.poll()); // c
// 打开此行
System.out.println(blockingQueue.poll()); // null
}
}
阻塞就是当队列满了的时候,生产者线程在put的时候,队列会一直阻塞生产线程,直到put数据或者响应中断退出,当队列空的时候,消费者线程在take的时候,队列会一直阻塞消费者线程直到队列可用。
public class BlockQueueDemo {
public static void main(String[] args) throws InterruptedException {
// 创建阻塞队列,3为阻塞队列的容量
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
// 返回false
// blockingQueue.put("x");
blockingQueue.take();
blockingQueue.take();
blockingQueue.take();
// 返回null
// blockingQueue.take();
}
超时:
public class BlockQueueDemo {
public static void main(String[] args) throws InterruptedException {
// 创建阻塞队列,3为阻塞队列的容量
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
// 表示2秒后队列中的值就会失效被移除
System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS)); // true
System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS)); // true
System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS)); // true
System.out.println(blockingQueue.offer("a", 2, TimeUnit.SECONDS)); // 两秒后返回false
}
}
二、SynchronousQueue
特性
SynchronousQueue队列中只能允许有一个元素,只有元素从队列中拿出来才允许再往里边放。下边看一下代码。大家可以自己运行感受一下。
public class BlockQueueDemo1 {
public static void main(String[] args) throws InterruptedException {
// 创建阻塞队列
BlockingQueue<String> blockingQueue = new SynchronousQueue<>();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " >>> put");
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName() + " >>> put");
blockingQueue.put("2");
System.out.println(Thread.currentThread().getName() + " >>> put");
blockingQueue.put("3");
}catch (Exception e) {
e.printStackTrace();
}
},"A").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(blockingQueue.take());
TimeUnit.SECONDS.sleep(2);
System.out.println(blockingQueue.take());
TimeUnit.SECONDS.sleep(2);
System.out.println(blockingQueue.take());
}catch (Exception e) {
e.printStackTrace();
}
},"B").start();
}
}
二、使用
以生产者消费者的例子来写一个阻塞队列的demo,场景是生产者每秒生产一个,消费者每秒消费一个,两者交替执行,持续5秒。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class BlockQueueDemo2 {
public static void main(String[] args) throws InterruptedException {
MyResource myResource = new MyResource(new ArrayBlockingQueue(10));
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "生产线程启动");
try {
myResource.myProd();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"prod").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "消费线程启动");
try {
myResource.myConsumer();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"consumer").start();
TimeUnit.SECONDS.sleep(5);
System.out.println("main线程停止");
myResource.stop();
}
}
class MyResource {
private volatile boolean FLAG = true;
private AtomicInteger num = new AtomicInteger();
private BlockingQueue<String> blockQueue;
public MyResource(BlockingQueue blockQueue) {
this.blockQueue = blockQueue;
System.out.println(blockQueue.getClass().getName());
}
public void myProd () throws InterruptedException {
String data;
boolean retValue;
while (FLAG) {
data = num.incrementAndGet() + "";
retValue = blockQueue.offer(data, 2L, TimeUnit.SECONDS);
if (retValue) {
System.out.println(Thread.currentThread().getName() + "插入队列"+ data +"成功");
} else {
System.out.println(Thread.currentThread().getName() + "插入队列"+ data +"失败");
}
TimeUnit.SECONDS.sleep(1);
}
System.out.println(Thread.currentThread().getName() + "主程序叫停,表示FLAG=false, 生产动作结束");
}
public void myConsumer () throws InterruptedException {
String result = null;
while (FLAG) {
result = blockQueue.poll(2L, TimeUnit.SECONDS);
if (result == null || result.equalsIgnoreCase("")) {
FLAG = false;
System.out.println(Thread.currentThread().getName() + "超过两秒钟没有消费,退出");
return;
}
System.out.println(Thread.currentThread().getName() + "消费队列"+ result +"成功");
}
System.out.println(Thread.currentThread().getName() + "主程序叫停,表示FLAG=false, 生产动作结束");
}
public void stop () {
this.FLAG = false;
}
}