java中PriorityBlockingQueue的入队知识点总结

yizhihongxing

下面是对 "java中PriorityBlockingQueue的入队知识点总结" 的详细讲解。

PriorityBlockingQueue的概述

PriorityBlockingQueuejava.util.concurrent 包中的一个类,它是一个具有优先级的无界阻塞队列,可以用来实现生产者-消费者模式中的队列。

PriorityBlockingQueue 使用数组实现,内部采用堆结构排序,可以根据元素自然排序或者通过构造函数指定的 Comparator 排序。它有以下主要特点:

  • 内部采用堆结构实现排序,具有很好的性能。
  • 可以通过两种方式排序:自然排序和指定比较器排序。
  • 内部使用 ReentrantLock(可重入锁)来保证线程安全。

PriorityBlockingQueue的入队操作

PriorityBlockingQueue 的入队方法是 offer()put(),两种方法的区别在于,当队列已满时,offer() 方法会返回 false,而 put() 方法则会阻塞。

offer()方法

offer() 方法可以向队尾插入一个元素,如果队列已满,则返回 false

public boolean offer(E e) {
    if (e == null)
        throw new NullPointerException();
    final ReentrantLock lock = this.lock;
    lock.lock();
    int n, cap;
    Object[] array;
    while ((n = size) >= (cap = (array = queue).length))
        tryGrow(array, cap);
    try {
        Comparator<? super E> cmp = comparator;
        if (cmp == null)
            siftUpComparable(n, e, array);
        else
            siftUpUsingComparator(n, e, array, cmp);
        size = n + 1;
        notEmpty.signal();
    } finally {
        lock.unlock();
    }
    return true;
}

tryGrow() 方法的作用是扩容。如果队列的长度已经等于容量,说明队列已满,此时需要扩容以容纳新的元素。

扩容的方法是通过创建一个新数组 newArray 来实现的,然后将原数组 queue 的元素复制到新数组中,最后将 newArray 赋值给 queue

private void tryGrow(Object[] array, int oldCap) {
    lock.unlock();
    Object[] newArray = null;
    if (allocationSpinLock == 0 &&
        UNSAFE.compareAndSwapInt(this, allocationSpinLockOffset, 0, 1)) {
        try {
            int newCap = oldCap + ((oldCap < 64) ?
                                   (oldCap + 2) :
                                   (oldCap >> 1));
            if (newCap >= MAX_ARRAY_SIZE)
                throw new OutOfMemoryError();
            int newAlloc = newCap - oldCap;
            newArray = new Object[newCap];
            System.arraycopy(array, 0, newArray, 0, Math.min(array.length, newAlloc));
        } finally {
            allocationSpinLock = 0;
        }
    }
    if (newArray == null)
        Thread.yield();
    lock.lock();
    if (queue == array) {
        queue = newArray;
        System.arraycopy(array, 0, newArray, 0, oldCap);
    }
}

while 循环的作用是,如果队列已满,就不断尝试扩容,直到新增元素可以被添加到队列中。

如果队列已满,但是扩容失败(例如在高并发的情况下,多个线程同时扩容),此时会让出 CPU 时间,等待其他线程释放锁,再重新尝试添加元素。

然后,调用 siftUpComparable()siftUpUsingComparator() 方法来对新增元素进行排序。如果没有指定比较器(即使用默认的自然排序),则调用 siftUpComparable() 方法。

private void siftUpComparable(int k, E x, Object[] array) {
    Comparable<? super E> key = (Comparable<? super E>) x;
    while (k > 0) {
        int parent = (k - 1) >>> 1;
        Object e = array[parent];
        if (key.compareTo((E) e) >= 0)
            break;
        array[k] = e;
        k = parent;
    }
    array[k] = key;
}

while 循环的条件是:如果新增元素比其父节点更小,则将父节点下移,继续比较,直到满足堆的性质。

最后,更新队列的元素数量,发出 notEmpty 的信号,表示队列已非空,并返回 true

put()方法

put() 方法将一个元素插入队列中,如果队列已满,则一直等待,直到队列有空闲位置。

public void put(E e) throws InterruptedException {
    offer(e); // 向队列尾部插入一个元素
    if (Thread.interrupted()) // 判断线程是否被打断,如果被打断,就抛出异常
        throw new InterruptedException();
}

put() 方法中,调用了 offer() 方法向队列尾部插入元素。如果队列已满,就不断尝试扩容或者等待,直到新增元素可以被添加到队列中。

最后,如果线程被中断,就抛出 InterruptedException 异常。

示例说明

下面以两个示例分别说明 PriorityBlockingQueue 的入队操作。

示例一

PriorityBlockingQueue<Integer> queue = new PriorityBlockingQueue<>(16);

for (int i = 0; i < 20; i++) {
    queue.offer(i);
    System.out.println("add " + i + ", size: " + queue.size());
}

结果输出:

add 0, size: 1
add 1, size: 2
add 2, size: 3
add 3, size: 4
add 4, size: 5
add 5, size: 6
add 6, size: 7
add 7, size: 8
add 8, size: 9
add 9, size: 10
add 10, size: 11
add 11, size: 12
add 12, size: 13
add 13, size: 14
add 14, size: 15
add 15, size: 16
add 16, size: 17
add 17, size: 18
add 18, size: 19
add 19, size: 20

PriorityBlockingQueue 的入队操作中,如果队列已满,就会进行扩容。在这个示例中,队列的初始容量是 16,当添加第 8 个元素时,队列已满,触发了扩容操作。从输出结果可以看出,每插入一个元素,队列的大小就会增加 1 。

示例二

PriorityBlockingQueue<Integer> queue = new PriorityBlockingQueue<>(16);

for (int i = 0; i < 20; i++) {
    queue.put(i);
    System.out.println("add " + i + ", size: " + queue.size());
}

System.out.println("--------------------");

for (int i = 20; i < 30; i++) {
    queue.put(i);
    System.out.println("add " + i + ", size: " + queue.size());
}

结果输出:

add 0, size: 1
add 1, size: 2
add 2, size: 3
add 3, size: 4
add 4, size: 5
add 5, size: 6
add 6, size: 7
add 7, size: 8
add 8, size: 9
add 9, size: 10
add 10, size: 11
add 11, size: 12
add 12, size: 13
add 13, size: 14
add 14, size: 15
add 15, size: 16
add 16, size: 17
add 17, size: 18
add 18, size: 19
add 19, size: 20
--------------------
add 20, size: 21
add 21, size: 22
add 22, size: 23
add 23, size: 24
add 24, size: 25
add 25, size: 26
add 26, size: 27
add 27, size: 28
add 28, size: 29
add 29, size: 30

在这个示例中,向一个容量为 16 的队列中插入了 20 个元素。由于队列的容量瞬间不足,应用了阻塞队列,当添加第 16 个元素时,队列已满,程序被阻塞,等待其他线程从队列中取出元素,直到取出足够的元素、空出足够的空间,才能继续向队列中添加元素。从输出结果可以看出,当队列中的元素个数小于容量时,插入元素的速度比取出元素的速度快;当队列中的元素个数等于容量时,新插入的元素会被阻塞,等待有空余空间的时候再插入。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java中PriorityBlockingQueue的入队知识点总结 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • WEB服务器大比拼,评析六大流派

    WEB服务器大比拼,评析六大流派 背景 在建设一个网站的时候,选择合适的WEB服务器是非常重要的一个决策。不同的WEB服务器适用于不同的场景,有些适合小型网站,有些适合高并发的大型网站。本文将会介绍六大流派中的常用WEB服务器,从各个方面来进行评析和对比,以便各位读者选择适合自己网站的WEB服务器。 流派一: Apache Apache是最早的自由WEB服务…

    Java 2023年6月15日
    00
  • 一篇文章教会你使用java爬取想要的资源

    使用Java进行网络数据爬取是一项常见的任务。本篇文章将详细讲解如何使用Java进行网络爬取,并提供两个示例说明。以下是爬虫攻略的详细步骤: 一、获取目标URL 首先,要确定你希望从哪个网站中获取数据。然后,你需要找到该网站中包含目标数据的具体页面。在本文的示例中,我将以 https://www.bilibili.com/ 作为目标网站。 二、分析网站结构 …

    Java 2023年5月23日
    00
  • Java Apache Commons报错“ConfigurationException”的原因与解决方法

    当使用Java的Apache Commons类库时,可能会遇到“ConfigurationException”错误。这个错误通常由以下原因之一起: 配置文件错误:如果配置文件错误,则可能会出现此错误。在这种情况下,需要检查配置文件以解决此问题。 配置项缺失:如果配置项缺失,则可能会出现此错误。在这种情况下,需要检查配置项以解决此问题。 以下是两个实例: 例1…

    Java 2023年5月5日
    00
  • SpringBoot整合SpringDataJPA

    Spring Boot整合Spring Data JPA Spring Data JPA是Spring Framework的一部分,它提供了一种简单的方式来访问关系型数据库。Spring Boot提供了对Spring Data JPA的自动配置支持,使得整合Spring Data JPA变得非常简单。在本文中,我们将介绍如何使用Spring Boot整合Sp…

    Java 2023年5月15日
    00
  • 如何实现Java的ArrayList经典实体类

    要实现Java的ArrayList经典实体类,我们需要完成以下步骤: 创建实体类:首先需要创建Java类作为实体类,用来描述我们希望在ArrayList中存储的数据结构。例如,我们创建一个书籍类Book,包括属性ISBN、书名、作者和价格。 public class Book { private String isbn; private String nam…

    Java 2023年5月26日
    00
  • 基于maven搭建一个ssm的web项目的详细图文教程

    下面是基于Maven搭建SSM(Web)项目的详细攻略: 前置条件 JDK 1.8+ 安装并配置好环境变量 Maven 安装并配置好环境变量 IDE,比如 IntelliJ IDEA 或 Eclipse 等可选 步骤一:创建Maven项目 打开IDE,选择创建Maven项目 选择Maven-archetype-webapp模板,输入项目信息,点击创建 步骤二…

    Java 2023年5月19日
    00
  • IntelliJ IDEA 创建 Java 项目及创建 Java 文件并运行的详细步骤

    下面是关于“IntelliJ IDEA 创建 Java 项目及创建 Java 文件并运行的详细步骤”的完整攻略: 步骤一:创建新的Java项目 打开 IntelliJ IDEA,进入欢迎界面,点击 “Create New Project”。 确认左侧栏选择 “Java”,并输入项目的名称,可以任意取。然后点击 “Next”。 在弹出的窗口中选择 “Proje…

    Java 2023年5月20日
    00
  • Spring Boot中是如何处理日期时间格式的

    Spring Boot中处理日期时间格式主要通过在实体类中使用注解@JsonFormat来完成。@JsonFormat是Jackson中的注解,可用于序列化和反序列化Java的日期和时间类型。 以下是处理日期时间格式的详细步骤: 在实体类的日期字段上添加@DateTimeFormat注解来指定日期时间格式,例如:yyyy-MM-dd。 在实体类的日期字段上添…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部