java 常用并发队列- PriorityQueue

PriorityQueue 是 Java 提供的一种基于优先级的队列数据结构。虽然 PriorityQueue 本身不是线程安全的,但它在单线程环境下非常常用。为了在多线程环境中使用,Java 提供了 PriorityBlockingQueue,它是 PriorityQueue 的线程安全版本。

1. PriorityQueue 的概述

1.1 什么是 PriorityQueue

PriorityQueue 是 Java 中实现 Queue 接口的一个类,它是一种基于优先级的队列,队列中的元素按优先级排序。默认情况下,PriorityQueue 是一个最小堆(Min-Heap),即队首元素是队列中最小的元素。当你从 PriorityQueue 中获取元素时,总是返回当前优先级最高(即值最小)的元素。

1.2 PriorityQueue 的主要特性
  • 优先级排序PriorityQueue 的元素并不是按插入顺序排列的,而是根据元素的自然顺序(或者通过传递给构造函数的比较器)进行排序。
  • 不允许 null 元素PriorityQueue 不允许 null 元素,因为 null 元素无法确定优先级。
  • 动态扩展PriorityQueue 的底层是一个基于数组的最小堆,因此它能够根据需要自动扩展其容量。
  • 非线程安全PriorityQueue 不是线程安全的,如果需要在多线程环境下使用,需要使用外部同步或使用 PriorityBlockingQueue
1.3 PriorityQueue 的使用示例

下面是一个简单的 PriorityQueue 使用示例:

import java.util.PriorityQueue;

public class PriorityQueueExample {
    public static void main(String[] args) {
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();

        priorityQueue.add(5);
        priorityQueue.add(1);
        priorityQueue.add(3);
        priorityQueue.add(2);

        System.out.println("PriorityQueue elements in order:");
        while (!priorityQueue.isEmpty()) {
            System.out.println(priorityQueue.poll());
        }
    }
}

输出结果为:

1
2
3
5

在这个例子中,PriorityQueue 将元素按自然顺序排序(从小到大),并按优先级顺序(最小的元素最先)输出。

2. PriorityQueue 的内部实现

2.1 基于堆的数据结构

PriorityQueue 的内部是通过堆(Heap)数据结构实现的,通常是最小堆。堆是一种特殊的完全二叉树,每个节点的值都小于或等于其子节点的值。

  • 最小堆:在最小堆中,堆顶元素是堆中最小的元素。这使得 PriorityQueuepeek()poll() 操作总是返回队列中优先级最高的元素。
2.2 动态数组扩展

PriorityQueue 底层是一个数组,当队列中的元素超过当前数组的容量时,数组会自动扩展。扩展后的新容量通常是旧容量的1.5倍左右。

2.3 插入与删除操作
  • 插入操作(addoffer:当向 PriorityQueue 插入一个元素时,元素首先被添加到堆的末尾,然后通过向上调整(Up-Heap)操作将该元素移动到正确的位置,以维持堆的性质。
  • 删除操作(poll:删除堆顶元素时,通常用堆的最后一个元素替换堆顶元素,然后通过向下调整(Down-Heap)操作恢复堆的性质。
2.4 自定义排序

PriorityQueue 默认使用自然顺序(通过元素的 compareTo 方法)对元素排序。如果需要自定义排序,可以在创建 PriorityQueue 时传入一个 Comparator

PriorityQueue<String> queue = new PriorityQueue<>((a, b) -> b.compareTo(a));

上面的例子创建了一个基于字符串的反序(降序)优先级队列。

3. 多线程环境中的 PriorityBlockingQueue

3.1 什么是 PriorityBlockingQueue

PriorityBlockingQueuePriorityQueue 的线程安全版本,它实现了 BlockingQueue 接口,支持多线程环境下的并发访问。与 PriorityQueue 不同,PriorityBlockingQueue 是无界的,并且支持阻塞的插入和取出操作。

3.2 PriorityBlockingQueue 的主要特性
  • 线程安全:通过内部使用的锁机制确保线程安全,多个线程可以同时进行插入和取出操作。
  • 无界队列PriorityBlockingQueue 是无界的,这意味着它可以容纳任意数量的元素,而不需要指定初始容量。
  • 阻塞操作:提供阻塞的 puttake 方法,当队列为空时,take 操作会阻塞直到有元素可用。
3.3 PriorityBlockingQueue 的使用示例

以下是一个 PriorityBlockingQueue 在多线程环境中的使用示例:

import java.util.concurrent.PriorityBlockingQueue;

public class PriorityBlockingQueueExample {
    public static void main(String[] args) {
        PriorityBlockingQueue<Integer> queue = new PriorityBlockingQueue<>();

        // Producer thread
        Thread producer = new Thread(() -> {
            for (int i = 10; i > 0; i--) {
                try {
                    queue.put(i);
                    System.out.println("Produced: " + i);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });

        // Consumer thread
        Thread consumer = new Thread(() -> {
            try {
                while (true) {
                    Integer value = queue.take();
                    System.out.println("Consumed: " + value);
                    if (value == 1) break;  // End loop after consuming 1
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        producer.start();
        consumer.start();
    }
}

在这个示例中,生产者线程将整数值按照从大到小的顺序放入 PriorityBlockingQueue,而消费者线程则以优先级顺序(从小到大)从队列中取出并消费这些值。

4. PriorityQueuePriorityBlockingQueue 的区别

4.1 线程安全性
  • PriorityQueue:非线程安全,在多线程环境下需要手动同步。
  • PriorityBlockingQueue:线程安全,内部实现了锁机制,适合多线程环境。
4.2 阻塞操作
  • PriorityQueue:不支持阻塞操作。poll() 在队列为空时立即返回 null
  • PriorityBlockingQueue:支持阻塞操作。take() 在队列为空时阻塞直到有元素可用。
4.3 有界与无界
  • PriorityQueue:有界或无界,取决于初始化时的容量设定,但默认会自动扩展。
  • PriorityBlockingQueue:无界,可以容纳任意数量的元素。

5. PriorityQueuePriorityBlockingQueue 的使用场景

5.1 PriorityQueue 的适用场景
  • 单线程任务调度:适合在单线程环境下对任务按优先级排序并依次处理。
  • 优先级缓存:适合在单线程中管理优先级队列,处理按优先级分配的任务或事件。
5.2 PriorityBlockingQueue 的适用场景
  • 多线程任务调度:适合在多线程环境下对任务按优先级排序并由多个线程并发处理。
  • 生产者-消费者模型:适合在需要优先级处理任务的生产者-消费者模型中使用,尤其是在多个生产者和消费者线程并发运行的场景中。

6. 总结

PriorityQueue 是 Java 中一个非常有用的基于优先级的队列,在单线程环境中表现出色。然而,在多线程环境下,PriorityQueue 由于非线程安全性,不能直接使用。PriorityBlockingQueue 则是 PriorityQueue 的线程安全版本,适合在多线程环境下使用。

在任务调度、优先级处理等场景中,选择合适的队列类型能够显著提升程序的性能和可靠性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Flying_Fish_Xuan

你的鼓励将是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值