通过前面文章的学习《一文详解「队列」,手撸队列的3种方法!》我们知道了队列(Queue)是先进先出(FIFO)的,并且我们可以用数组、链表还有 List 的方式来实现自定义队列,那么本文我们来系统的学习一下官方是如何实现队列的。
Java 中的队列有很多,例如:ArrayBlockingQueue、LinkedBlockingQueue、PriorityQueue、DelayQueue、SynchronousQueue 等,那它们的作用是什么?又是如何分类的呢?
其实 Java 中的这些队列可以从不同的维度进行分类,例如可以从阻塞和非阻塞进行分类,也可以从有界和无界进行分类,而本文将从队列的功能上进行分类,例如:优先队列、普通队列、双端队列、延迟队列等。
虽然本文的重点是从功能上对队列进行解读,但其它分类也是 Java 中的重要概念,所以我们先来了解一下它们。
阻塞队列和非阻塞队列阻塞队列(Blocking Queue)提供了可阻塞的 put 和 take 方法,它们与可定时的 offer 和 poll 是等价的。如果队列满了 put 方法会被阻塞等到有空间可用再将元素插入;如果队列是空的,那么 take 方法也会阻塞,直到有元素可用。当队列永远不会被充满时,put 方法和 take 方法就永远不会阻塞。
我们可以从队列的名称中知道此队列是否为阻塞队列,阻塞队列中包含 BlockingQueue 关键字,比如以下这些:
ArrayBlockingQueue
LinkedBlockingQueue
PriorityBlockingQueue
.......
阻塞队列功能演示接下来我们来演示一下当阻塞队列的容量满了之后会怎样,示例代码如下:
import java.util.Date; import java.util.concurrent.ArrayBlockingQueue; public class BlockingTest { public static void main(String[] args) throws InterruptedException { // 创建一个长度为 5 的阻塞队列 ArrayBlockingQueue q1 = new ArrayBlockingQueue(5); // 新创建一个线程执行入列 new Thread(() -> { // 循环 10 次 for (int i = 0; i < 10; i++) { try { q1.put(i); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(new Date() + " | ArrayBlockingQueue Size:" + q1.size()); } System.out.println(new Date() + " | For End."); }).start(); // 新创建一个线程执行出列 new Thread(() -> { for (int i = 0; i < 5; i++) { try { // 休眠 1S Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (!q1.isEmpty()) { try { q1.take(); // 出列 } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }以上代码的执行结果如下:
Mon Oct 19 20:16:12 CST 2020 | ArrayBlockingQueue Size:1
Mon Oct 19 20:16:12 CST 2020 | ArrayBlockingQueue Size:2
Mon Oct 19 20:16:12 CST 2020 | ArrayBlockingQueue Size:3
Mon Oct 19 20:16:12 CST 2020 | ArrayBlockingQueue Size:4
Mon Oct 19 20:16:12 CST 2020 | ArrayBlockingQueue Size:5
Mon Oct 19 20:16:13 CST 2020 | ArrayBlockingQueue Size:5
Mon Oct 19 20:16:14 CST 2020 | ArrayBlockingQueue Size:5
Mon Oct 19 20:16:15 CST 2020 | ArrayBlockingQueue Size:5
Mon Oct 19 20:16:16 CST 2020 | ArrayBlockingQueue Size:5
Mon Oct 19 20:16:17 CST 2020 | ArrayBlockingQueue Size:5
Mon Oct 19 20:16:17 CST 2020 | For End.
从上述结果可以看出,当 ArrayBlockingQueue 队列满了之后就会进入阻塞,当过了 1 秒有元素从队列中移除之后,才会将新的元素入列。
非阻塞队列非阻塞队列也就是普通队列,它的名字中不会包含 BlockingQueue 关键字,并且它不会包含 put 和 take 方法,当队列满之后如果还有新元素入列会直接返回错误,并不会阻塞的等待着添加元素,如下图所示:
非阻塞队列的典型代表是 ConcurrentLinkedQueue 和 PriorityQueue。 有界队列和无界队列
有界队列:是指有固定大小的队列,比如设定了固定大小的 ArrayBlockingQueue,又或者大小为 0 的 SynchronousQueue。