来,回顾下 Java 中的各种队列…

简介: 当用户超时未支付时,给用户发提醒消息。另一种场景是,超时未付款,订单自动取消。通常,订单创建的时候可以向延迟队列种插入一条消息,到时间自动执行。其实,也可以用临时表,把这些未支付的订单放到一个临时表中,或者Redis,然后定时任务去扫描。这里我们用延时队列来做,RocketMQ有延时队列,RibbitMQ也可以实现,Java自带的也有延时队列,接下来就回顾一下各种队列。

当用户超时未支付时,给用户发提醒消息。


另一种场景是,超时未付款,订单自动取消。


通常,订单创建的时候可以向延迟队列种插入一条消息,到时间自动执行。


其实,也可以用临时表,把这些未支付的订单放到一个临时表中,或者Redis,然后定时任务去扫描。


这里我们用延时队列来做,RocketMQ有延时队列,RibbitMQ也可以实现,Java自带的也有延时队列,接下来就回顾一下各种队列。


image.png


Queue

队列是一种集合。


除了基本的集合操作以外,队列还提供了额外的插入、提取和检查操作。


队列的每个方法都以两种形式存在:一种是当操作失败时抛异常,另一种是返回一个特定的值(null或者false,取决于具体操作)。


后一种形式的插入操作是专门设计用于有界队列实现的,在大多情况下,插入操作不会失败。


image.png


队列通常(但不一定)以FIFO(先进先出)的方式对元素进行排序。例外情况包括优先级队列(根据提供的比较器对元素进行排序或元素的自然排序)和LIFO队列(或堆栈),对LIFO进行排序(后进先出)。


无论使用哪种顺序,队列的开头都是该元素,可以通过调用remove()或poll()将其删除。在FIFO队列中,所有新元素都插入队列的尾部。其他种类的队列可能使用不同的放置规则。 每个Queue实现必须指定其排序属性。无论使用哪种顺序,都可以通过调用remove()或poll()来删除队列开头的元素。


在FIFO队列中,所有新元素都插入到队列的尾部。其他类型的队列可能使用不同的放置规则。每个队列实现都必须指定其排序属性。


offer方法在可以的情况下会向队列种插入一个元素,否则返回false。这不同于Collection.add方法,后者只能通过抛异常来添加元素。offer方法设计用于在正常情况下(而不是在例外情况下)发生故障时,例如在固定容量(或者“有界”)队列种使用。


remove()和poll()方法删除并返回队头元素。当队列为空时,remove()抛出异常,而poll()返回null。


element()和peek()方法返回队头元素。


PriorityQueue

PriorityQueue是一个无界优先级队列是基于优先级堆的。优先级队列种的元素根据自然顺序进行排序,或者通过在队列构建时提供的Comparator进行排序,当然这取决于使用哪种构造函数。优先级队列不允许空(null)元素。一个依赖自然顺序的优先级队列也不允许插入不可比较的对象。


优先级队列的队头元素是最小的元素,如果有多个元素并列最小,那么队头是它们其中之一。


优先级队列是无界的,但是有一个内部容量来控制用于在队列上存储元素的数组的大小。它总是至少与队列大小一样大。将元素添加到优先级队列时,其容量会自动增长。


BlockingQueue

这种队列还支持以下操作:在检索元素时等待队列变为非空,并在存储元素时等待队列中的空间变为可用。


BlockingQueue方法有四种形式,它们以不同的方式处理操作,这些操作无法立即满足,但将来可能会满足:一种抛出异常,第二种返回特殊值(null或false,取决于具体操作),第三种阻塞当前线程,直到操作成功为止;第四种阻塞当前线程,超时则放弃。 下表总结了这些方法:


image.png


阻塞队列不接受空元素,如果你试图add , put 或者 offer 一个null,将会抛NullPointerException。


阻塞队列是线程安全的。所有排队方法都使用内部锁或者其他形式的并发控制来保证以原子方式实现它们的效果。


阻塞队列被设计主要用于生产者-消费者队列。


下面是一个典型的生产者-消费者方案:

package com.example;
import java.text.MessageFormat;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
 * @author ChengJianSheng
 * @date 2020/12/15
 */
public class Setup {
    public static void main(String[] args) {
        BlockingQueue<Bread> queue = new ArrayBlockingQueue<>(5);
        Producer p1 = new Producer(queue);
        Producer p2 = new Producer(queue);
        Consumer c1 = new Consumer(queue);
        Consumer c2 = new Consumer(queue);
        new Thread(p1, "p1").start();
        new Thread(p2, "p2").start();
        new Thread(c1, "c1").start();
        new Thread(c2, "c2").start();
    }
}
class Bread {
}
/**
 * 生产者
 */
class Producer implements Runnable {
    private final BlockingQueue<Bread> queue;
    public Producer(BlockingQueue<Bread> queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        try {
            while (true) {
                queue.put(produce());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public Bread produce() {
        try {
            Thread.sleep(Math.round(2000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return new Bread();
    }
}
/**
 * 消费者
 */
class Consumer implements Runnable {
    private final BlockingQueue<Bread> queue;
    public Consumer(BlockingQueue<Bread> queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        try {
            while (true) {
                consume(queue.take());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void consume(Bread bread) {
        try {
            Thread.sleep(Math.round(2000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

ArrayBlockingQueue

ArrayBlockingQueue是用数组实现的有界阻塞队列。这种队列中的元素按FIFO(先进先出)排序。队头是在队列中停留最长时间的元素。队尾是在队列中停留时间最短的元素。新元素插入到队列的尾部,并且队列检索操作在队列的头部获取元素。


这是一个经典的“有界缓冲区”,其中固定大小的数组包含由生产者插入并由消费者提取的元素。 创建后,容量将无法更改。 试图将一个元素放入一个已满的队列将导致操作阻塞; 试图从空队列中取出一个元素也会阻塞。


这个类支持一个可选的公平性策略,用于对等待的生产者和消费者线程进行排序。默认情况下,不保证这个顺序。然而,将公平性设置为true的队列将按FIFO顺序授予线程访问权。公平性通常会降低吞吐量,但会降低可变性并避免饥饿。


LinkedBlockingQueue

LinkedBlockingQueue是一个基于链表实现的可选边界的阻塞队列。


PriorityBlockingQueue

PriorityBlockingQueue是一个无界阻塞队列,它使用与PriorityQueue相同的排序规则,并提供阻塞检索操作。


DelayQueue

DelayQueue是一种由延迟元素组成的无界阻塞队列,在该队列中,仅当元素的延迟到期时才可以使用该元素。队头是已经过期的延迟元素,它已过期时间最长。如果没有过期的延迟,则队列没有头部,此时调用poll将返回null。当调用元素的getDelay(TimeUnit.NANOSECONDS)方法返回值小于或等于0时,就会发生过期。即使元素没有过期,也不能用take或者poll将其删除。


AbstractQueuedSynchronizer

AbstractQueuedSynchronizer提供了一个框架来实现依赖于先进先出(FIFO)等待队列的阻塞锁和相关的同步器(信号灯,事件等)。该类旨在为大多数依赖单个原子int值表示状态的同步器提供有用的基础。子类必须定义更改此状态的受保护方法,并定义该状态对于获取或释放此对象而言意味着什么。 鉴于这些,此类中的其他方法将执行所有排队和阻塞机制。 子类可以维护其他状态字段,但是仅跟踪关于同步的使用方法getState(),setState(int)和compareAndSetState(int,int)操作的原子更新的int值。


小结

1、Queue是一个集合,队列的每个方法都有两种形式,一种是抛异常,另一种是返回一个特定的值。


2、PriorityQueue是一个无界优先级队列,默认情况下,队列种的元素按自然顺序排序,或者根据提供的Comparator进行排序。也就是说,优先级队列种的元素都是经过排序的,排序规则可以自己指定,同时队列种的元素都必须是可排序的。


3、BlockingQueue是一个阻塞队列,向已满的队列种插入元素时会阻塞,向空队列中取元素时也会阻塞;阻塞队列被设计主要用于生产者-消费者队列。


4、ArrayBlockingQueue是用数组实现的有界阻塞队列,队列种的元素按FIFO(先进先出)排序。


5、LinkedBlockingQueue是用链表实现的可选边界的阻塞队列。


6、PriorityBlockingQueue相当于是阻塞队列和优先级队列的合体,排序规则与优先级队列相同。


7、DelayQueue延时队列中的元素都有一个有效期,只有当过了有效期才可以使用该元素。


相关文章
|
8月前
|
前端开发 Java
java实现队列数据结构代码详解
本文详细解析了Java中队列数据结构的实现,包括队列的基本概念、应用场景及代码实现。队列是一种遵循“先进先出”原则的线性结构,支持在队尾插入和队头删除操作。文章介绍了顺序队列与链式队列,并重点分析了循环队列的实现方式以解决溢出问题。通过具体代码示例(如`enqueue`入队和`dequeue`出队),展示了队列的操作逻辑,帮助读者深入理解其工作机制。
281 1
|
算法 Java
Java数据结构——队列
Java数据结构——队列
125 4
|
11月前
|
存储 监控 Java
JAVA线程池有哪些队列? 以及它们的适用场景案例
不同的线程池队列有着各自的特点和适用场景,在实际使用线程池时,需要根据具体的业务需求、系统资源状况以及对任务执行顺序、响应时间等方面的要求,合理选择相应的队列来构建线程池,以实现高效的任务处理。
561 12
|
存储 安全 Java
【用Java学习数据结构系列】探索栈和队列的无尽秘密
【用Java学习数据结构系列】探索栈和队列的无尽秘密
140 2
|
Java API 容器
JAVA并发编程系列(10)Condition条件队列-并发协作者
本文通过一线大厂面试真题,模拟消费者-生产者的场景,通过简洁的代码演示,帮助读者快速理解并复用。文章还详细解释了Condition与Object.wait()、notify()的区别,并探讨了Condition的核心原理及其实现机制。
java中的队列
这篇文章通过Java代码示例介绍了使用数组实现队列操作,包括队列的初始化、入队、出队、判断队列满和空以及遍历队列的方法。
java中的队列
|
存储 算法 Java
【用Java学习数据结构系列】用堆实现优先级队列
【用Java学习数据结构系列】用堆实现优先级队列
195 0
|
Java 开发者
揭秘!LinkedList是如何华丽变身成为Java队列之王的?
【6月更文挑战第18天】Java的`LinkedList`既是列表也是队列之星,实现`Queue`接口,支持FIFO操作。其内部的双向链表结构确保了添加/移除元素的高效性(O(1)),适合作为队列使用。它线程不安全,但可通过同步包装用于多线程环境。此外,`LinkedList`还能灵活变身栈或双端队列,提供多种数据结构功能。
232 11
|
设计模式 安全 Java
Java面试题:请解释Java中的线程池以及为什么要使用线程池?请解释Java中的内存模型以及如何避免内存泄漏?请解释Java中的并发工具包以及如何实现一个简单的线程安全队列?
Java面试题:请解释Java中的线程池以及为什么要使用线程池?请解释Java中的内存模型以及如何避免内存泄漏?请解释Java中的并发工具包以及如何实现一个简单的线程安全队列?
176 1
|
安全 Java
Java Queue新玩法:用LinkedList打造高效队列,让你的代码飞起来!
【6月更文挑战第18天】Java集合框架中的`LinkedList`不仅是列表,还可作为高效队列。由于其在链表两端进行添加/移除操作的时间复杂度为O(1),故适合实现并发环境下的任务队列。通过案例展示了如何创建、添加任务及确保线程安全,揭示了`LinkedList`提升代码性能的秘密,特别是在多线程应用中的价值。
181 4