温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Java中的5大队列是什么

发布时间:2021-10-26 09:34:22 阅读:139 作者:iii 栏目:编程语言
Java开发者专用服务器限时活动,0元免费领,库存有限,领完即止! 点击查看>>

这篇文章主要讲解了“Java中的5大队列是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java中的5大队列是什么”吧!

阻塞队列和非阻塞队列

阻塞队列(Blocking Queue)提供了可阻塞的 puttake 方法,它们与可定时的 offerpoll 是等价的。如果队列满了 put 方法会被阻塞等到有空间可用再将元素插入;如果队列是空的,那么 take 方法也会阻塞,直到有元素可用。当队列永远不会被充满时,put 方法和 take 方法就永远不会阻塞。

Java中的5大队列是什么  


我们可以从队列的名称中知道此队列是否为阻塞队列,阻塞队列中包含 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 方法,当队列满之后如果还有新元素入列会直接返回错误,并不会阻塞的等待着添加元素,如下图所示:

Java中的5大队列是什么

非阻塞队列的典型代表是 ConcurrentLinkedQueue 和 PriorityQueue。 

有界队列和无界队列

有界队列:是指有固定大小的队列,比如设定了固定大小的 ArrayBlockingQueue,又或者大小为 0 的 SynchronousQueue

Java中的5大队列是什么

无界队列:指的是没有设置固定大小的队列,但其实如果没有设置固定大小也是有默认值的,只不过默认值是 Integer.MAX_VALUE,当然实际的使用中不会有这么大的容量(超过 Integer.MAX_VALUE),所以从使用者的角度来看相当于 “无界”的。

Java中的5大队列是什么    

按功能分类

接下来就是本文的重点了,我们以功能来划分一下队列,它可以被分为:普通队列、优先队列、双端队列、延迟队列、其他队列等,接下来我们分别来看。 

1.普通队列

普通队列(Queue)是指实现了先进先出的基本队列,例如 ArrayBlockingQueue 和 LinkedBlockingQueue,其中 ArrayBlockingQueue 是用数组实现的普通队列,如下图所示:

Java中的5大队列是什么

LinkedBlockingQueue 是使用链表实现的普通队列,如下图所示:

Java中的5大队列是什么  
 

常用方法

普通队列中的常用方法有以下这些:

  • offer():添加元素,如果队列已满直接返回 false,队列未满则直接插入并返回 true;
  • poll():删除并返回队头元素,当队列为空返回 null;
  • add():添加元素,此方法是对 offer 方法的简单封装,如果队列已满,抛出 IllegalStateException 异常;
  • remove():直接删除队头元素;
  • put():添加元素,如果队列已经满,则会阻塞等待插入;
  • take():删除并返回队头元素,当队列为空,则会阻塞等待;
  • peek():查询队头元素,但不会进行删除;
  • element():对 peek 方法进行简单封装,如果队头元素存在则取出并不删除,如果不存在抛出 NoSuchElementException 异常。

注意:一般情况下 offer() 和 poll() 方法配合使用,put() 和 take() 阻塞方法配合使用,add() 和 remove() 方法会配合使用,程序中常用的是 offer() 和 poll() 方法,因此这两个方法比较友好,不会报错

接下来我们以 LinkedBlockingQueue 为例,演示一下普通队列的使用:

import java.util.concurrent.LinkedBlockingQueue;static class LinkedBlockingQueueTest {    public static void main(String[] args) {        LinkedBlockingQueue queue = new LinkedBlockingQueue();        queue.offer("Hello");        queue.offer("Java");        queue.offer("中文社群");        while (!queue.isEmpty()) {            System.out.println(queue.poll());        }    }}
 

以上代码的执行结果如下:

Hello

Java

中文社群

 
2.双端队列

双端队列(Deque)是指队列的头部和尾部都可以同时入队和出队的数据结构,如下图所示:

Java中的5大队列是什么

接下来我们来演示一下双端队列 LinkedBlockingDeque 的使用:

import java.util.concurrent.LinkedBlockingDeque;/**  * 双端队列示例  */static class LinkedBlockingDequeTest {    public static void main(String[] args) {        // 创建一个双端队列        LinkedBlockingDeque deque = new LinkedBlockingDeque();        deque.offer("offer"); // 插入首个元素        deque.offerFirst("offerFirst"); // 队头插入元素        deque.offerLast("offerLast"); // 队尾插入元素        while (!deque.isEmpty()) {            // 从头遍历打印            System.out.println(deque.poll());        }    }}
 

以上代码的执行结果如下:

offerFirst

offer

offerLast

 
3.优先队列

优先队列(PriorityQueue)是一种特殊的队列,它并不是先进先出的,而是优先级高的元素先出队。

优先队列是根据二叉堆实现的,二叉堆的数据结构如下图所示:

Java中的5大队列是什么

二叉堆分为两种类型:一种是最大堆一种是最小堆。以上展示的是最大堆,在最大堆中,任意一个父节点的值都大于等于它左右子节点的值。

因为优先队列是基于二叉堆实现的,因此它可以将优先级最好的元素先出队。

接下来我们来演示一下优先队列的使用:

import java.util.PriorityQueue;public class PriorityQueueTest {    // 自定义的实体类    static class Viper {        private int id; // id        private String name; // 名称        private int level; // 等级        public Viper(int id, String name, int level) {            this.id = id;            this.name = name;            this.level = level;        }        public int getId() {            return id;        }        public void setId(int id) {            this.id = id;        }        public String getName() {            return name;        }        public void setName(String name) {            this.name = name;        }        public int getLevel() {            return level;        }        public void setLevel(int level) {            this.level = level;        }    }    public static void main(String[] args) {  PriorityQueue queue = new PriorityQueue(10, new Comparator<Viper>() {            @Override            public int compare(Viper v1, Viper v2) {                // 设置优先级规则(倒序,等级越高权限越大)                return v2.getLevel() - v1.getLevel();            }        });        // 构建实体类        Viper v1 = new Viper(1"Java"1);        Viper v2 = new Viper(2"MySQL"5);        Viper v3 = new Viper(3"Redis"3);        // 入列        queue.offer(v1);        queue.offer(v2);        queue.offer(v3);        while (!queue.isEmpty()) {            // 遍历名称            Viper item = (Viper) queue.poll();            System.out.println("Name:" + item.getName() +                               " Level:" + item.getLevel());        }    }}
 

以上代码的执行结果如下:

Name:MySQL Level:5

Name:Redis Level:3

Name:Java Level:1

从上述结果可以看出,优先队列的出队是不考虑入队顺序的,它始终遵循的是优先级高的元素先出队

 
4.延迟队列

延迟队列(DelayQueue)是基于优先队列 PriorityQueue 实现的,它可以看作是一种以时间为度量单位的优先的队列,当入队的元素到达指定的延迟时间之后方可出队。

Java中的5大队列是什么  


我们来演示一下延迟队列的使用:

import lombok.Getter;import lombok.Setter;import java.text.DateFormat;import java.util.Date;import java.util.concurrent.DelayQueue;import java.util.concurrent.Delayed;import java.util.concurrent.TimeUnit;public class CustomDelayQueue {    // 延迟消息队列    private static DelayQueue delayQueue = new DelayQueue();    public static void main(String[] args) throws InterruptedException {        producer(); // 调用生产者        consumer(); // 调用消费者    }    // 生产者    public static void producer() {        // 添加消息        delayQueue.put(new MyDelay(1000"消息1"));        delayQueue.put(new MyDelay(3000"消息2"));    }    // 消费者    public static void consumer() throws InterruptedException {        System.out.println("开始执行时间:" +                DateFormat.getDateTimeInstance().format(new Date()));        while (!delayQueue.isEmpty()) {            System.out.println(delayQueue.take());        }        System.out.println("结束执行时间:" +                DateFormat.getDateTimeInstance().format(new Date()));    }    static class MyDelay implements Delayed {        // 延迟截止时间(单位:毫秒)        long delayTime = System.currentTimeMillis();        // 借助 lombok 实现        @Getter        @Setter        private String msg;        /**         * 初始化         * @param delayTime 设置延迟执行时间         * @param msg       执行的消息         */        public MyDelay(long delayTime, String msg) {            this.delayTime = (this.delayTime + delayTime);            this.msg = msg;        }        // 获取剩余时间        @Override        public long getDelay(TimeUnit unit) {            return unit.convert(delayTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);        }        // 队列里元素的排序依据        @Override        public int compareTo(Delayed o) {            if (this.getDelay(TimeUnit.MILLISECONDS) > o.getDelay(TimeUnit.MILLISECONDS)) {                return 1;            } else if (this.getDelay(TimeUnit.MILLISECONDS) < o.getDelay(TimeUnit.MILLISECONDS)) {                return -1;            } else {                return 0;            }        }        @Override        public String toString() {            return this.msg;        }    }}
 

以上代码的执行结果如下:

开始执行时间:2020-10-20 20:17:28

消息1

消息2

结束执行时间:2020-10-20 20:17:31

从上述结束执行时间和开始执行时间可以看出,消息 1 和消息 2 都正常实现了延迟执行的功能。

 
5.其他队列

在 Java 的队列中有一个比较特殊的队列 SynchronousQueue,它的特别之处在于它内部没有容器,每次进行 put() 数据后(添加数据),必须等待另一个线程拿走数据后才可以再次添加数据,它的使用示例如下:

import java.util.concurrent.SynchronousQueue;public class SynchronousQueueTest {    public static void main(String[] args) {        SynchronousQueue queue = new SynchronousQueue();        // 入队        new Thread(() -> {            for (int i = 0; i < 3; i++) {                try {                    System.out.println(new Date() + ",元素入队");                    queue.put("Data " + i);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }).start();        // 出队        new Thread(() -> {            while (true) {                try {                    Thread.sleep(1000);                    System.out.println(new Date() + ",元素出队:" + queue.take());                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }).start();    }}
 

以上代码的执行结果如下:

Mon Oct 19 21:00:21 CST 2020,元素入队

Mon Oct 19 21:00:22 CST 2020,元素出队:Data 0

Mon Oct 19 21:00:22 CST 2020,元素入队

Mon Oct 19 21:00:23 CST 2020,元素出队:Data 1

Mon Oct 19 21:00:23 CST 2020,元素入队

Mon Oct 19 21:00:24 CST 2020,元素出队:Data 2

从上述结果可以看出,当有一个元素入队之后,只有等到另一个线程将元素出队之后,新的元素才能再次入队。

感谢各位的阅读,以上就是“Java中的5大队列是什么”的内容了,经过本文的学习后,相信大家对Java中的5大队列是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

原文链接:https://my.oschina.net/u/3471412/blog/4689559

AI

开发者交流群×