当前位置: 首页 > news >正文

Java Collection/Executor DelayedWorkQueue 总结

前言


 相关系列

  • 《Java & Collection & 目录》
  • 《Java & Executor & 目录》
  • 《Java & Collection/Executor & DelayedWorkQueue & 源码》
  • 《Java & Collection/Executor & DelayedWorkQueue & 总结》
  • 《Java & Collection/Executor & DelayedWorkQueue & 问题》
     

 涉及内容

  • 《Java & Collection & 总结》
  • 《Java & Collection & Queue & 总结》
  • 《Java & Collection & AbstractQueue & 总结》
  • 《Java & Collection & PriorityQueue & 总结》
  • 《Java & Collection/Executor & BlockingQueue & 总结》
  • 《Java & Collection/Executor & DelayQueue & 总结》
  • 《Java & Executor & 总结》
  • 《Java & Executor & RunnableScheduledFuture & 总结》
  • 《Java & Executor & ThreadPoolExecutor & 总结》
  • 《Java & Executor & ScheduledThreadPoolExecutor & 总结》
  • 《Java & Lock/AQS & ReentrantLock & 总结》
  • 《Java & Other & Delayed & 总结》
  • 《Java & Other & Comparable & 总结》
     

 注意

    DelayedWorkQueue @ 延迟工作队列类是Executor @ 执行器框架在DelayedQueue @ 延迟队列类的基础上为实现/优化ScheduledThreadPoolExecutor @ 调度线程池执行器类而设计的专项优化类,其功能/实现与延迟队列类高度一致,因此本文不会再对“延迟”功能进行重复阐述,而是着重讲解优化项的相关内容,故而读者在阅读本文前必须先对延迟队列类的功能/实现有所了解,相关资料可通过上方链接获得。
 
 

概述


 简介

    延迟工作队列类是BlockingQueue @ 阻塞队列接口的实现类之一,基于数组实现。与常规阻塞队列接口实现类不同,延迟工作队列类并非独立的类实现,而是以静态内部类的形式存在于调度线程池执行器类中。与此同时其还并非是公共权限的开放类,默认权限意味着其专为调度线程池执行器类及其兄弟/子类而设计,禁止在其之外的类中被使用,因此开发者通常是无法在日常开发中使用延迟工作队列类的,除非使用反射等非常规机制。

    延迟工作队列类提供与延迟队列类一致的“延迟”功能,即元素只有在指定延迟时间到期后才允许被头部移除。实际上,延迟工作队列类与延迟队列类在代码实现上拥有接近完全一致的相似度,基本可以直接认为是延迟工作队列类拷贝了延迟队列类的代码。这不经令人产生疑惑…既然二者如此相似,那延迟工作队列类存在的意义又是什么呢?实际上延迟工作队列类是在延迟队列类的基础上针对调度线程池执行器类的内部运行机制而被设计出来专项优化类,其相比延迟队列类而言其在元素类型/排序方面对元素定位进行了订制,使得元素内部移除操作的时间复杂度由原本的O(n)降低为了O(log n),并在GC方面也有一定的额外收益,该知识点的内容会在下文讲解优化时详述。

    延迟工作队列类不支持保存任意类型的元素,固定的Runnable @ 可运行接口泛型意味着其只能保存可运行。延迟工作队列类的泛型被指定为可运行接口是因为其只会被作为调度线程池执行器类及其兄弟/子类的任务存储容器使用,但也是因为相同原因,延迟工作队列真正保存的元素实际是可运行接口子接口RunnableScheduledFuture @ 可运行调度未来的对象,因为调度线程池执行器中的任务必然会以可运行调度未来的形式存在,而这不禁会令人产生“为何延迟工作队列类不直接将泛型设置为可运行调度未来接口”的疑惑,该知识点会在下文讲解优化时详述。

    延迟工作队列类不允许存null,或者说阻塞队列接口的所有实现类都不允许存null。null被作为poll()及peek()方法表示延迟工作队列不存在元素的标记值,因此所有阻塞队列接口实现类都不允许存null。

    延迟工作队列类是无界队列,其最大容量理论上只受限于堆内存的大小。延迟工作队列类的默认初始容量为16,并且只能在创建时隐式/自动设置而无法显式指定。虽然基于长度固定的数组实现,但延迟工作队列类内部存在扩容机制,因此也被纳入无界队列的范畴中。由于在具体实现上受到数组及int类型的物理限制,因此虽说是无界队列,但实际其最大容量仅可扩容至Integer.MAX_VALUE。扩容的本质是创建长度更大的新元素数组来代替旧元素数组,并将旧元素数组中的元素迁移至新元素数组。容量的具体增长规则如下:

新容量 = 旧容量 + 旧容量 >> 1(约1.5倍)

    延迟工作队列类是线程安全的,或者说阻塞队列接口的所有实现类都是线程安全的,其接口定义中强制要求实现类必须线程安全。延迟工作队列类采用“单锁”线程安全机制,即使用一个ReentrantLock @ 可重入锁来保证整体的线程安全。

    延迟工作队列类的迭代器是弱一致性的,即可能迭代到已移除的元素及无法迭代到新插入的元素。延迟工作队列的迭代器实现非常直接(或者说过于直接了),其会直接将数据拷贝一份快照存入生成的迭代器中以进行迭代。这么做的好处是迭代器的实现非常的简单,但缺点也明显,当延迟工作队列元素总数较大或生成的迭代器数量较多时对内存的消耗会非常严重。

    延迟工作队列类虽然与阻塞队列接口一样都被纳入执行器框架的范畴,但同时也是Collection @ 集框架的成员。
 

 方法的不同形式

    所谓方法的不同形式是指方法在保证自身核心操作不变的情况下实现多种不同的回应形式来应对不同场景下的使用要求。方法的不同形式实际上是Queue @ 队列接口的定义,阻塞队列接口拓展了该定义,而延迟工作队列类实现了该定义。例如对于插入,当容量不足时,有些场景希望在失败时抛出异常;而有些场景则希望能直接返回失败的标记值;而有些场景又希望可以等待至存在可用容量后成功新增为止…正是因为这些不同场景的存在,方法的不同形式才应运而生。方法最多可以拥有四种不同回应形式,这四种回应形式的具体设定如下:

异常 —— 队列接口定义 —— 当不满足操作条件时直接抛出异常;
特殊值 —— 队列接口定义 —— 当不满足操作条件时直接返回失败标记值。例如之所以不允许存null就是因为null被作为了操作失败时的标记值;
阻塞(无限等待) —— 阻塞队列接口定义 —— 当不满足操作条件时无限等待,直至满足操作条件后执行;
超时(有限等待) —— 阻塞队列接口定义 —— 当不满足操作条件时有限等待,如果在指定等待时间之前满足操作条件则执行;否则返回失败标记值。
 

 与延迟队列类的区别

  • 延迟队列类底层采用优先级队列类实现,而延迟工作队列类底层没有采用优先级队列类实现,其在内部大范围复写了相似代码并进行了专项优化;
  • 延迟队列类/优先级队列类的默认初始容量为11,而延迟工作队列类的默认初始容量为16;
  • 延迟队列类根据“旧容量 < 64”与否进行“2倍加2”与“1.5倍”的双规则进行扩容,而延迟工作队列类只按“1.5倍”的单规则进行扩容;
  • 延迟队列类的泛型被指定为Delayed @ 延迟接口,而延迟工作队列类的泛型被直接指定为可运行接口,虽然其实际保存的可运行调度未来同样也是延迟接口的对象;
  • 延迟队列类元素被内部移除的时间复杂度为O(n),因为在内部移除前需要先遍历数组找到指定元素的首个实例;而延迟工作队列类在元素类型/排序方面对元素定位进行了订制,使得内部移除的元素可以被直接定位而无需进行遍历查找,从而令其时间复杂度和只需计算排序消耗的头部移除一样为O(log n)。
     
     

使用


 创建

  • public DelayedWorkQueue() —— 创建延迟工作队列。

 

 插入

    注意!本文的“元素”与“延迟到期元素”是不同的概念。元素指队列所有元素,而延迟到期元素则是指队列中剩余延迟时间小于等于0的元素。

  • public boolean add(E e) —— 新增 —— 向当前延迟工作队列的尾部插入指定元素,并根据指定元素的剩余延迟时间按小顶堆规则将之排序到合适位置。该方法是尾部插入方法“异常”形式的实现,当当前延迟工作队列存在剩余容量时插入并返回true;否则抛出非法状态异常。虽说定义如此,但实际由于延迟工作队列类是真正的无界队列,最大容量只受限于堆内存的大小,故而永远不会抛出非法状态异常,而只会在堆内存不足时抛出内存溢出错误。

  • public boolean offer(E e) —— 提供 —— 向当前延迟工作队列的尾部插入指定元素,并根据指定元素的剩余延迟时间按小顶堆规则将之排序到合适位置。该方法是尾部插入方法“特殊值”形式的实现,当当前延迟工作队列存在剩余容量时插入并返回true;否则返回false。虽说定义如此,但实际由于延迟工作队列类是真正的无界队列,最大容量只受限于堆内存的大小,故而永远不会返回false,而只会在堆内存不足时抛出内存溢出错误。

  • public void put(E e) —— 放置 —— 向当前延迟工作队列的尾部插入指定元素,并根据指定元素的剩余延迟时间按小顶堆规则将之排序到合适位置。该方法是尾部插入方法“阻塞”形式的实现,当当前延迟工作队列存在剩余容量时插入;否则无限等待至存在剩余容量为止。虽说定义如此,但实际由于延迟工作队列类是真正的无界队列,最大容量只受限于堆内存的大小,故而永远不会等待,而只会在堆内存不足时抛出内存溢出错误。

  • public boolean offer(E e, long timeout, TimeUnit unit) —— 提供 —— 向当前延迟工作队列的尾部插入指定元素,并根据指定元素的剩余延迟时间按小顶堆规则将之排序到合适位置。该方法是尾部插入方法“超时”形式的实现,当当前延迟工作队列存在剩余容量时插入并返回true;否则在指定等待时间内有限等待至存在剩余容量为止,超出指定等待时间则返回false。虽说定义如此,但实际由于延迟工作队列类是真正的无界队列,最大容量只受限于堆内存的大小,故而永远不会等待/返回false,而只会在堆内存不足时抛出内存溢出错误。
     

 移除

  • public E remove() —— 移除 —— 从当前延迟工作队列的头部移除并获取剩余延迟时间最小的延迟到期元素,并触发后续元素的重排序。该方法是头部移除方法“异常”形式的实现,当当前延迟工作队列存在延迟到期元素时移除并返回头延迟到期元素;否则抛出无元素异常。

  • public E poll() —— 轮询 —— 从当前延迟工作队列的头部移除并获取剩余延迟时间最小的延迟到期元素,并触发后续元素的重排序。该方法是头部移除方法“特殊值”形式的实现,当当前延迟工作队列存在延迟到期元素时移除并返回头延迟到期元素;否则返回null。

  • public E take() throws InterruptedException —— 拿取 —— 从当前延迟工作队列的头部移除并获取剩余延迟时间最小的延迟到期元素,并触发后续元素的重排序。该方法是头部移除方法“阻塞”形式的实现,当当前延迟工作队列存在延迟到期元素时移除并返回头延迟到期元素;否则无限等待至存在延迟到期元素为止。

  • public E poll(long timeout, TimeUnit unit) throws InterruptedException —— 轮询 —— 从当前延迟工作队列的头部移除并获取剩余延迟时间最小的延迟到期元素,并触发后续元素的重排序。该方法是头部移除方法“超时”形式的实现,当当前延迟工作队列存在延迟到期元素时移除并返回头延迟到期元素;否则在指定等待时间内有限等待至存在延迟到期元素为止,超出指定等待时间则返回null。

  • public boolean remove(Object o) —— 移除 —— 从当前延迟工作队列中按迭代器顺序移除首个指定元素,当移除成功时返回true;否则返回false。注意是元素,而非延迟到期元素。
        由于指定元素可能处于任意位置(不一定是头/尾),因此被称为内部移除。内部移除并不是常用的方法:一是其不符合FIFO的数据操作方式;二是各类实现为了提高性能可能会使用各种优化策略,而remove(Object o)方法往往无法适配这些策略,导致性能较/极差。

  • public void clear() —— 清理 —— 从当前延迟工作队列中移除所有元素。注意是元素,而非延迟到期元素。
     

 检查

  • public E element() —— 元素 —— 从当前延迟工作队列的头部获取剩余延迟时间最小的元素。该方法是检查方法头部位置“异常”形式的实现,当当前延迟工作队列存在元素时返回头元素;否则抛出无元素异常。注意该方法不同于头部移除方法,其获取的是元素而非延迟到期元素,因此即使头元素延迟尚未到期也会将之返回,故而只会在延迟工作队列为空时抛出无元素异常。

  • public E peek() —— 窥视 —— 从当前延迟工作队列的头部获取剩余延迟时间最小的元素。该方法是检查方法头部位置“特殊值”形式的实现,当当前延迟工作队列存在元素时返回头元素;否则返回null。注意该方法不同于头部移除方法,其获取的是元素而非延迟到期元素,因此即使头元素延迟尚未到期也会将之返回,故而只会在延迟工作队列为空时抛出无元素异常。
     

 流失

  • public int drainTo(Collection<? super E> c) —— 流失 —— 将当前延迟工作队列中的所有延迟到期元素流失到指定集中,并返回流失的延迟到期元素总数。被流失的延迟到期元素将不再存在于当前延迟工作队列中。

  • public int drainTo(Collection<? super E> c, int maxElements) —— 流失 —— 将当前延迟工作队列中最多指定数量的延迟到期元素流失到指定集中,并返回流失的延迟到期元素总数。被流失的延迟到期元素将不再存在于当前延迟工作队列中。
     

 查询

  • public int size() —— 大小 —— 获取当前延迟工作队列的元素总数。注意是元素,而非延迟到期元素。

  • public boolean isEmpty() —— 是否为空 —— 判断当前延迟工作队列是否为空,是则返回true;否则返回false。

  • public int remainingCapacity() —— 剩余容量 —— 获取当前延迟工作队列的剩余容量。由于延迟工作队列类是无界队列,因此该方法将永远返回Integer.MAX_VALUE。

  • public Object[] toArray() —— 转化数组 —— 获取按迭代器顺序包含当前延迟工作队列中所有元素的新数组。注意是元素,而非延迟到期元素。

  • public T[] toArray(T[] a) —— 转化数组 —— 获取按迭代器顺序包含当前延迟工作队列中所有元素的泛型数组。如果参数泛型数组长度足以容纳所有元素,则令之承载所有元素后返回。并且如果参数泛型数组的长度大于当前延迟工作队列的元素总数,则将已承载所有元素的参数泛型数组的size索引位置设置为null,表示从当前延迟工作队列中承载的元素到此为止。当然,该方案只对不允许保存null元素的集有效。如果参数泛型数组的长度不足以承载所有元素,则重分配一个相同泛型且长度与当前延迟工作队列元素总数相同的新泛型数组以承载所有元素后返回。注意是元素,而非延迟到期元素。
     

 迭代器

  • public Iterator<E> iterator() —— 迭代器 —— 创建可遍历当前延迟工作队列中元素的迭代器。注意是元素,而非延迟到期元素。

    事实上,上文中只列举了大部分常用方法。由于延迟工作队列类是集接口的实现类,因此其也实现了其定义的所有方法,例如contains(Object o)、removeAll(Collection<?> c)、containsAll(Collection<?> c)等。但由于这些方法的执行效率不高,并且与延迟工作队列类的主流使用方式并不兼容/兼容性差,因此通常是不推荐使用的,有兴趣的童鞋可以去查看源码实现。
 
 

优化


在这里插入图片描述

 可运行调度未来接口

    延迟工作队列实际只会保存可运行调度未来。虽然泛型被指定为可运行接口,但由于延迟工作队列类实际只在调度线程池执行器类中被使用,并且调度线程池执行器类限定任务必须为可运行调度未来接口类型,因此延迟工作队列实际只会保存可运行调度未来。这不禁令人产生疑惑…延迟工作队列类为什么不直接将泛型设置为可运行调度未来接口呢?

    这确实是一个非常令人费解的问题,因为从设计上看,延迟工作队列类的元素类型需要具备两方面的特性:一是可以代表任务,因为延迟工作队列具体就是在线程池执行器作为任务容器使用的;二是可以实现延迟,因为这本就是延迟工作队列类所提供的基本功能。因此无论从什么角度上看可运行调度未来接口同时作为可运行接口/延迟接口的子接口都更合适作为延迟工作队列类的泛型…但实际情况显然不是如此。

    将可运行接口作为延迟工作队列类的泛型最大的问题在于需要对每个插入的元素都强制转化为可运行调度未来接口类型,因为首先延迟工作队列类设计用于保存元素的[queue @ 队列/元素数组]本就是可运行调度未来接口类型,因此可运行接口对象并无法直接插入;此外实现元素延迟也需要调用compareTo(T o)方法进行元素间剩余延迟时间的比较以实现排序,而可运行显然也并不存在该方法。我们先不考虑这种父类至子类的强行转化可能导致失败,单单从问题上看就完全可以通过将泛型指定为可运行调度未来接口的方式解决,所以强转这一步行为理论上是完全可以不必要的。

    将可运行接口作为延迟工作队列类的泛型主要是基于扩展/兼容性的考量。该说法实际上只是我个人的主观猜测,因为对于一个难以理解的问题,通常可以从实现/性能/开销/结构/使用五个方向去考虑。其中对于实现/性能/开销/使用是可以直接排除的,因为泛型指定为可运行接口不但增加了实现的复杂度,还导致强转降低了性能/增大了开销,很显然这都是负面影响,因此唯有在结构方面还可能带来正面收益,因为可运行接口作为可运行调度未来接口的父接口是契合“面向接口编程”的思想的。

    “面向接口编程”思想的本质是“面向父/超类/接口编程”,其推荐使用父/超类/接口类型的变量来承接子/实现类的实例以降低代码的耦合度并提升扩展/兼容性。虽然在JDK1.8的源码中延迟工作队列类并没有被调度线程池执行器类以外的API所使用,但很显然作者也并未打算将之彻底锁死在调度线程池执行器类中,该论点的依据有二:一是延迟工作队列类的访问权限为default而非protected,这使得延迟工作队列类可以被调度线程池执行器类的兄弟类所使用;二是开发者可以在外部通过getQueue()方法获取到内置在调度线程池执行器中的延迟工作队列实例并执行操作。在上述情况下保证插入的元素一定为可运行调度任务接口对象是无法/难以做到的…由此我们就可以猜测之所以使用可运行接口会被作为泛型就是作者为后期在调度线程池执行器类以外的API中使用延迟工作队列类所做出的预留/提示/标记。而如果真是如此,那到时候延迟工作队列类必然还需要进行再一次的改版。因为目前除了强转外,延迟工作队列类的内部逻辑完全是基于元素为可运行调度任务接口对象的前提下实现的,后期显然还需要再增加对非可运行调度任务接口对象的支持。

/*** 主方法** @param args 参数集*/
public static void main(String[] args) {// 获取内置于调度线程池执行器中的延迟工作队列实例。ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(20);BlockingQueue<Runnable> blockingQueue = scheduledThreadPoolExecutor.getQueue();// 向延迟工作队列中添加一个可运行,该行为会抛出转化异常。Runnable runnable = () -> System.out.println("我是一个无法被强转为可运行调度未来的可运行...");blockingQueue.add(runnable);
}

在这里插入图片描述

 

 调度未来任务类

    延迟工作队列类基于ScheduledFutureTask @ 调度未来任务类对元素定位进行了优化。调度未来任务类是可运行调度未来接口的实现类,也是调度线程池执行器类默认使用的可运行调度未来接口实现类,即调度线程池执行器会将递交的任务封装为调度未来任务后执行。开发者可以通过“装饰”令调度线程池执行器类使用其它可运行调度未来接口实现类,该知识点会在调度线程池执行器类的专项文章中详述,但是并不建议这么做,因为延迟工作队列类优化了对调度未来任务类型元素的定位,即如果元素为调度未来任务则延迟工作队列可以在常数时间内快速定位到其在[队列/元素数组]中的位置,换句话说就是查找的时间复杂度为O(1)。这种优化虽然付出了一个额外字段的开销,但对查找性能的提升却非常明显,因为在正常情况下想要定位一个指定元素就只能通过时间复杂度为O(n)的遍历操作实现。延迟工作队列类之所以会设计该类优化是因为调度线程池执行器在运行过程中可能会因为周期性任务的取消而相对频繁地对元素进行内部移除,而由于内部移除需要先对元素进行定位,因此大量遍历必然会对调度线程池执行器的整体性能造成影响。而[堆索引]的存在避免了调度未来任务的遍历定位,从而就使得调度未来任务的内部移除流程可以与头部移除的流程相近,故而在时间复杂度上也由原本的O(n)下降为了O(log n)。O(log n)是元素被内部移除后延迟工作队列在移除位置的基础上对剩余元素进行下排序的时间复杂度,虽说与头部移除的时间复杂度相同,但实际由于下排序的起点位于[队列/元素数组]内部而非头部,因此实际时间消耗会比头部移除更少。

    调度未来任务在[队列/元素数组]中的所在位置会保存在[heapIndex @ 堆索引]中,并随着在上/下排序而更新。调度未来任务的[堆索引]是其可被直接定位的核心原因,这使得其在需要被定位时可直接通过[堆索引]在[队列/元素数组]的指定位置上进行查找。但这并不意味着找到的调度未来任务与指定调度未来任务就是相同的,因为上文虽然说过延迟工作队列类是调度线程池执行器类的内部类,并且实例也只会在其内部创建,但开发者依然可以通过执行器的getQueue()方法获取到延迟工作队列。因此从一个延迟工作队列中获取调度未来任务并将之从另一个延迟工作队列中移除的情况是可能存在的…故而如果发现并不相同则依然要进行遍历定位。此外,由于[堆索引]只能唯一保存,因此一个调度未来任务“在被移除前”不允许再次插入同一个延迟工作队列中。因为后一次插入会覆盖前一次插入维护的[堆索引],而这可能导致操作出现异常…以内部移除为例:内部移除的作用是移除指定元素迭代器顺序的首个实例,而如果一个调度未来任务在一个延迟工作队列中被多次保存,那就可能出现移除非首个实例的情况。当然,如果是非调度未来任务类型的元素那也没有这种要求。

    [堆索引]可以间接减少延迟工作队列中的垃圾遗留。所谓的垃圾是指无效元素及其关联的相应对象,[堆索引]的存在并无法直接减少垃圾在延迟工作队列中的遗留,即其在内部运行流程方面并不会对GC产生额外收益。而之所以说[堆索引]可以间接减少延迟工作队列中的垃圾遗留是因为其大幅优化调度未来任务的定位后使得内部移除不再是一个低性能的操作,因此调度线程池执行器完全可以通过频繁调用延迟工作队列的remove(Object x)方法将无效调度未来任务内部移除来降低其中垃圾遗留的总量,而无需等待其被自然排序到[队列/元素数组]的顶部后再头部移除。

相关文章:

Java Collection/Executor DelayedWorkQueue 总结

前言 相关系列 《Java & Collection & 目录》《Java & Executor & 目录》《Java & Collection/Executor & DelayedWorkQueue & 源码》《Java & Collection/Executor & DelayedWorkQueue & 总结》《Java & Collection/Executor &a…...

《TCP/IP网络编程》学习笔记 | Chapter 1:理解网络编程和套接字

《TCP/IP网络编程》学习笔记 | Chapter 1&#xff1a;理解网络编程和套接字 《TCP/IP网络编程》学习笔记 | Chapter 1&#xff1a;理解网络编程和套接字基本概念服务端客户端 基于 Linux 平台的 "Hello world!" 服务端和客户端基于 Linux 的文件操作打开文件关闭文件…...

服务端监控工具:Nmon使用方法

在性能测试过程中&#xff0c;对服务端的各项资源使用情况进行监控是很重要的一环。这篇博客&#xff0c;介绍下服务端监控工具&#xff1a;nmon的使用方法。 一、认识nmon 1、简介 nmon是一种在AIX与各种Linux操作系统上广泛使用的监控与分析工具&#xff0c;它能在系统运行…...

Java中的线程安全问题(如果想知道Java中有关线程安全问题的基本知识,那么只看这一篇就足够了!)

前言&#xff1a;多线程编程已经广泛开始使用&#xff0c;其可以充分利用系统资源来提升效率&#xff0c;但是线程安全问题也随之出现&#xff0c;它直接影响了程序的正确性和稳定性&#xff0c;需要对其进行深入的理解与解决。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解…...

基础设施即代码(IaC)在Python自动化运维中的应用探讨

基础设施即代码&#xff08;IaC&#xff09;在Python自动化运维中的应用探讨 目录 &#x1f310; IaC概念与工具介绍&#x1f40d; 使用Python实现基础设施自动化&#x1f4e6; 版本控制与基础设施管理的最佳实践&#x1f504; 部署环境的一致性与可复现性 1. &#x1f310; …...

浅谈路由器

路由器是一种网络设备&#xff0c;它在网络中起着至关重要的作用&#xff0c;主要功能包括&#xff1a; 1、数据转发&#xff1a;路由器的主要任务是将数据包从一个网络转发到另一个网络。它根据数据包的目的地址来决定将数据包发送到哪个网络。 2、路径选择&#xff1a;路由器…...

openGauss数据库-头歌实验1-1 初识openGauss

一、历史与特性 任务描述 本关任务&#xff1a;了解openGauss的发展历史以及相关特性。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.openGauss的发展历程&#xff0c;2.openGauss的功能特性。 发展历程 2019年9月19日在华为全联接大会上&#xff0c;…...

QT找不到ffmpeg链接库解决方法

error: undefined reference to avformat_network_init() 一个神奇的报错&#xff0c;查了很久&#xff0c;检查步骤&#xff1a; 1、检查了 pro工程文件 2、链接库的真实性和正确性 在main.cpp中调用没有报错&#xff0c;在其它cpp文件中调用就报错。 破案了&#xff0c;…...

消息队列-Rabbitmq(消息发送,消息接收)

将来我们开发业务功能的时候&#xff0c;肯定不会在控制台收发消息&#xff0c;而是应该基于编程的方式。由于RabbitMQ采用了AMQP协议&#xff0c;因此它具备跨语言的特性。任何语言只要遵循AMQP协议收发消息&#xff0c;都可以与RabbitMQ交互。并且RabbitMQ官方也提供了各种不…...

2、顶点着色器之视图矩阵

1、作用&#xff1a;将物体从世界坐标系转换到相机坐标系&#xff0c;相当于从世界坐标系转换到相机的局部(本地)坐标系。 2、基于LookAt函数的视图矩阵&#xff1a; 相机位置eye&#xff1a;(ex,ey,ez)&#xff0c;世界坐标系下的位置 目标位置center&#xff1a;(cx,cy,cz…...

crontab实现2026年开始每个月1号执行一次

要在 crontab 中设置一个任务&#xff0c;使其从 2026 年开始每个月的 1 号执行一次&#xff0c;可以使用以下格式&#xff1a; 0 0 1 * * <你的命令>这条规则的解释如下&#xff1a; 0 0&#xff1a;表示在每个月的 1 号的零点&#xff08;00:00&#xff09;执行。1&a…...

计算机网络803-(5)运输层

目录 一.运输层的两个主要协议&#xff1a;TCP 与 UDP 1.TCP/IP 的运输层有两个不同的协议&#xff1a; 2.端口号(protocol port number) &#xff08;1&#xff09;软件端口与硬件端口 &#xff08;2&#xff09;TCP 的端口 &#xff08;3&#xff09;三类端口 二.用户…...

八 MyBatis中接口代理机制及使用

八、MyBatis中接口代理机制及使用 实际上&#xff0c;第七章所讲内容mybatis内部已经实现了。直接调用以下代码即可获取dao接口的代理类&#xff1a; AccountDao accountDao (AccountDao)sqlSession.getMapper(AccountDao.class);使用以上代码的前提是&#xff1a;AccountMa…...

【解决】Ubuntu18.04 卸载python之后桌面异常且终端无法打开,重启后进入tty1,没有图形化界面

我因为python版本太过于混乱 &#xff08;都是为了学习os&#xff09; &#xff0c;3.6—3.9版本我都安装了&#xff0c;指向关系也很混乱&#xff0c;本着“重装是最不会乱”的原则&#xff0c;我把全部版本都卸载了。然后装了3.9 发现终端打不开了&#xff0c;火狐浏览器的图…...

OpenEmbedded、yocto和poky是什么关系?

Yocto项目是基于OpenEmbedded构建系统发展而来的。Yocto采用了OpenEmbedded的许多核心概念和工具&#xff0c;比如BitBake构建工具。BitBake在这两个系统中都是用于解析和处理recipes文件&#xff0c;这些recipes文件包含了软件包构建的指令、依赖关系、安装步骤等内容。 它们…...

记录页面——一个蛮好看的登录页(uni-app)

效果图 <template><view class"container"><view class"flex-col login-box"><view class"flex-col" style"width: 80%"><view class"flex-col"><text class"welcome-text-font&qu…...

Android文件选择器[超级轻量级FilePicker测试没有问题][挣扎解决自带文件管理器获取不到绝对地址问题而是返回msf%3A1000038197]

超级轻量级FilePicker测试没有问题 本文摘录于&#xff1a;https://blog.csdn.net/gitblog_00365/article/details/141449437只是做学习备份之用&#xff0c;绝无抄袭之意&#xff0c;有疑惑请联系本人&#xff01; 今天真的是发了疯的找文件管理器,因为调用系统自带的文件管理…...

【论文速读】| RED QUEEN: 保护大语言模型免受隐蔽多轮越狱攻击

基本信息 原文标题&#xff1a;RED QUEEN: Safeguarding Large Language Models against Concealed Multi-Turn Jailbreaking 原文作者&#xff1a;Yifan Jiang, Kriti Aggarwal, Tanmay Laud, Kashif Munir, Jay Pujara, Subhabrata Mukherjee 作者单位&#xff1a;Hippocr…...

39.第二阶段x86游戏实战2-HOOK实现主线程调用

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要…...

wordpress argon主题美化方面

1、页面前端额外CSS&#xff1a; /*字体*/ font-face {font-family: myFont1; src:url(https://blog.yangmumu.com/css/fonts/Dancing.ttf) ;font-display: swap; } font-face {font-family: myFont2; src:url(https://blog.yangmumu.com/css/fonts/Regular.ttf) ;font-displa…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

门静脉高压——表现

一、门静脉高压表现 00:01 1. 门静脉构成 00:13 组成结构&#xff1a;由肠系膜上静脉和脾静脉汇合构成&#xff0c;是肝脏血液供应的主要来源。淤血后果&#xff1a;门静脉淤血会同时导致脾静脉和肠系膜上静脉淤血&#xff0c;引发后续系列症状。 2. 脾大和脾功能亢进 00:46 …...

C++中vector类型的介绍和使用

文章目录 一、vector 类型的简介1.1 基本介绍1.2 常见用法示例1.3 常见成员函数简表 二、vector 数据的插入2.1 push_back() —— 在尾部插入一个元素2.2 emplace_back() —— 在尾部“就地”构造对象2.3 insert() —— 在任意位置插入一个或多个元素2.4 emplace() —— 在任意…...