Java ~ Collection/Executor ~ DelayQueue【总结】
前言
文章
- 相关系列:《Java ~ Collection【目录】》(持续更新)
- 相关系列:《Java ~ Executor【目录】》(持续更新)
- 相关系列:《Java ~ Collection/Executor ~ DelayQueue【源码】》(学习过程/多有漏误/仅作参考/不再更新)
- 相关系列:《Java ~ Collection/Executor ~ DelayQueue【总结】》(学习总结/最新最准/持续更新)
- 相关系列:《Java ~ Collection/Executor ~ DelayQueue【问题】》(学习解答/持续更新)
- 涉及内容:《Java ~ Collection【总结】》
- 涉及内容:《Java ~ Collection ~ Queue【总结】》
- 涉及内容:《Java ~ Collection ~ PriorityQueue【总结】》
- 涉及内容:《Java ~ Collection/Executor ~ BlockingQueue【总结】》
- 涉及内容:《Java ~ Executor【总结】》
- 涉及内容:《Java ~ AQS ~ ReentrantLock【总结】》
- 涉及内容:《Java ~ Other ~ Delayed【总结】》
- 涉及内容:《Java ~ Other ~ Comparable【总结】》
一 概述
简介
DelayQueue(延迟队列)类是BlockingQueue(阻塞队列)接口的实现类之一,基于数组实现,特点是元素会被延迟头部移除。延迟队列类不支持任意类型的元素,其元素被强制指定为Delayed(延迟)接口对象,即延迟接口实现类对象,否则会抛出类型转换异常。之所以强制类型是因为延迟队列会调用延迟接口定义的getDelay(TimeUnit unit)方法来获取元素的剩余延迟来实现精确延迟,因此延迟接口实现类必须实现该方法以返回有效的剩余延迟。除此之外,由于底层使用PriorityQueue(优先级队列)类的原因,延迟接口实现类还必须实现Comparable(比较能力)接口定义的compareTo(T o)方法以实现元素之间剩余延迟时间的比较,该知识点的详细内容会在下文详述。
延迟队列类不允许存null值,或者说阻塞队列接口的所有实现类都不允许存null值。null被作为poll()及peek()方法表示延迟队列不存在元素的标记值,因此所有阻塞队列接口实现类都不允许存null值。
延迟队列类是无界队列,意味着其最大容量理论上只受限于堆内存的大小。延迟队列类底层使用优先级队列类实现,由于其扩容机制的存在,延迟队列类也被纳入无界队列的范围中。但虽说如此,优先级队列类在实现中还受到数组实现与int类型影响,因此延迟队列的最大容量实际上为Integer.MAX_VALUE。由于其无界队列的定义,为了掩盖实际实现中受到的限制,当其保存的元素总数触达上限时会模拟堆内存不足的场景手动抛出内存溢出错误。
延迟队列类是线程安全的,或者说阻塞队列接口的所有实现类都是线程安全的,其接口定义中强制要求实现类必须线程安全。延迟队列类采用“单锁”线程安全机制,即使用一个ReentrantLock(可重入锁)类对象来保证整体的线程安全。
延迟队列类的迭代器是弱一致性,即可能迭代到已移除的元素及无法迭代到新插入的元素。延迟队列的迭代器实现非常直接(或者说过于直接了),其会直接将数据拷贝一份快照存入生成的迭代器中以进行迭代。这么做的好处是迭代器的实现非常的简单,但缺点也明显,当延迟队列的元素总数较大或生成的迭代器数量较多时对内存的消耗会非常严重。
延迟队列类虽然与阻塞队列接口一样都被纳入Executor(执行器)框架的范畴,但同时也是Collection(集)框架的成员。
结构
方法的不同形式
方法的不同形式实际上是BlockingQueue(阻塞队列)接口的定义,链接阻塞双端队列只是继承了这个定义而已。所谓方法的不同形式,是指方法在保证自身核心操作不变的情况下实现了多种不同的回应形式来应对不同场景下的使用要求。例如对于插入,当容量不足时,有些场景希望在失败时抛出异常;而有些场景则希望能直接返回失败的标记值;而有些场景又希望可以等待直至有可用空间后成功新增为止…正因如此,BlockingQueue(阻塞队列)接口特意提供了四种不同的形式风格以满足不同场景下的使用需求,因此一个方法最多(并非所有方法都实现了四种形式)可能有四种不同回应形式。具体四种回应形式如下:
异常 —— 队列接口定义 —— 当不满足操作条件时直接抛出异常;
特殊值 —— 队列接口定义 —— 当不满足操作条件时直接返回失败标记值。例如之所以不允许存null值就是因为null被作为了操作失败时的标记值;
阻塞(无限等待) —— 阻塞队列接口定义 —— 当不满足操作条件时无限等待,直至满足操作条件后执行;
超时(有限等待) —— 阻塞队列接口定义 —— 当不满足操作条件时有限等待,如果在指定等待时间之前满足操作条件则执行;否则返回失败标记值。
二 创建
-
public DelayQueue() —— 创建延迟队列。
-
public DelayQueue(Collection<? extends E> c) —— 创建按迭代器顺序包含指定集中所有元素的延迟队列。
三 方法
需要提前说明的是:本文中的元素与延迟到期元素在概念上并不相同,元素指延迟队列中的所有元素,而延迟到期元素指的是延迟队列中已延迟到期的元素,因此延迟到期元素是元素的子集,在下文中要时刻注意这两者的区别。
插入
-
public boolean add(E e) —— 新增 —— 向当前延迟队列的尾部插入指定元素,并根据指定元素的剩余延迟按小顶堆规则将之排序到合适位置。该方法是尾部插入方法“异常”形式的实现,当当前延迟队列存在剩余容量时插入并返回true;否则抛出非法状态异常。虽说定义如此,但实际由于延迟队列类是真正的无界队列,最大容量只受限于堆内存的大小,故而永远不会抛出非法状态异常,而只会在堆内存不足时抛出内存溢出错误。由于延迟队列类基于优先级队列类实现,因此最大容量实际还受限于Integer.MAX_VALUE。当元素总数触达该上限时,为了掩盖实际实现上的限制,会手动抛出内存溢出错误。
-
public boolean offer(E e) —— 提供 —— 向当前延迟队列的尾部插入指定元素,并根据指定元素的剩余延迟按小顶堆规则将之排序到合适位置。该方法是尾部插入方法“特殊值”形式的实现,当当前延迟队列存在剩余容量时插入并返回true;否则返回false。虽说定义如此,但实际由于延迟队列类是真正的无界队列,最大容量只受限于堆内存的大小,故而永远不会抛出非法状态异常,而只会在堆内存不足时抛出内存溢出错误。由于延迟队列类基于优先级队列类实现,因此最大容量实际还受限于Integer.MAX_VALUE。当元素总数触达该上限时,为了掩盖实际实现上的限制,会手动抛出内存溢出错误。
-
public void put(E e) —— 放置 —— 向当前延迟队列的尾部插入指定元素,并根据指定元素的剩余延迟按小顶堆规则将之排序到合适位置。该方法是尾部插入方法“阻塞”形式的实现,当当前延迟队列存在剩余容量时插入;否则无限等待至存在剩余容量为止。虽说定义如此,但实际由于延迟队列类是真正的无界队列,最大容量只受限于堆内存的大小,故而永远不会抛出非法状态异常,而只会在堆内存不足时抛出内存溢出错误。由于延迟队列类基于优先级队列类实现,因此最大容量实际还受限于Integer.MAX_VALUE。当元素总数触达该上限时,为了掩盖实际实现上的限制,会手动抛出内存溢出错误。
-
public boolean offer(E e, long timeout, TimeUnit unit) —— 提供 —— 向当前延迟队列的尾部插入指定元素,并根据指定元素的剩余延迟按小顶堆规则将之排序到合适位置。该方法是尾部插入方法“超时”形式的实现,当当前延迟队列存在剩余容量时插入并返回true;否则在指定等待时间内有限等待至存在剩余容量为止,超出指定等待时间则返回false。虽说定义如此,但实际由于延迟队列类是真正的无界队列,最大容量只受限于堆内存的大小,故而永远不会抛出非法状态异常,而只会在堆内存不足时抛出内存溢出错误。由于延迟队列类基于优先级队列类实现,因此最大容量实际还受限于Integer.MAX_VALUE。当元素总数触达该上限时,为了掩盖实际实现上的限制,会手动抛出内存溢出错误。
移除
- public E remove() —— 移除 —— 从当前延迟队列的头部移除并获取剩余延迟最小的延迟到期元素,并触发后续元素的重排序。该方法是头部移除方法“异常”形式的实现,当当前延迟队列存在延迟到期元素时移除并返回头延迟到期元素;否则抛出无元素异常。
延迟队列类并没有自实现remove()方法,而是直接使用了父类AbstractQueue(抽象队列)抽象类的实现。在实现中其调用了头部移除方法“特殊值”形式的poll()方法来达成目的,使得所有抽象队列抽象类的子类只需实现poll()方法后就可以正常调用remove()方法。这种代码结构是设计模式的一种,被称为“模板模式”。
/*** Retrieves and removes the head of this queue. This method differs from {@link #poll poll} only in that it throws an exception if this queue is empty.* 检索并移除队列的头。该方法不同于poll()方法,如果队列为空时其会抛出一个异常。* <p>* This implementation returns the result of <tt>poll</tt> unless the queue is empty.* 除非队列为空,否则该实现返回poll()的结果。** @return the head of this queue 队列的头(元素)* @throws NoSuchElementException if this queue is empty* 无元素异常:如果队列为空*/
public E remove() {// 调用poll()方法获取元素。E x = poll();if (x != null)// 如果元素存在,直接返回。return x;else// 如果元素不存在,抛出无元素异常。throw new NoSuchElementException();
}
-
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() —— 元素 —— 从当前延迟队列的头部获取剩余延迟最小的元素。该方法是头部检查方法“异常”形式的实现,当当前延迟队列存在元素时返回头元素;否则抛出无元素异常。注意该方法不同于头部移除方法,其获取的是元素而非延迟到期元素,因此即使头元素延迟尚未到期也会将之返回,故而只会在延迟队列为空时抛出无元素异常。
与remove()方法相同,延迟队列类并没有自实现element()方法,而是直接使用了父类AbstractQueue(抽象队列)抽象类的实现(具体源码如下)。在实现中其调用了检查方法“特殊值”形式的peek()方法来达成目的,使得所有抽象队列抽象类的子类只需实现peek()方法后就可以正常调用element()方法。这种代码结构是设计模式的一种,被称为“模板模式”。
/*** Retrieves, but does not remove, the head of this queue. This method differs from {@link #peek peek} only in that it throws an exception if this* queue is empty.* 检索,但不移除队列的头(元素)。该方法不同于peek()方法,如果队列为空时其会抛出一个异常。* <p>* This implementation returns the result of <tt>peek</tt> unless the queue is empty.* 除非队列为空,否则该实现返回peek()的结果。** @return the head of this queue 队列的头(元素)* @throws NoSuchElementException if this queue is empty* 无元素异常:如果队列为空* @Description: 元素:用于返回队列的头元素(但不移除)。当队列中不存在元素时抛出无元素异常。*/
public E element() {E x = peek();if (x != null)return x;elsethrow new NoSuchElementException();
}
- public E peek() —— 窥视 —— 从当前延迟队列的头部获取剩余延迟最小的元素。该方法是头部检查方法“特殊值”形式的实现,当当前延迟队列存在元素时返回头元素;否则返回null。注意该方法不同于头部移除方法,其获取的是元素而非延迟到期元素,因此即使头元素延迟尚未到期也会将之返回,故而只会在延迟队列为空时抛出无元素异常。
流失
-
public int drainTo(Collection<? super E> c) —— 流失 —— 将当前延迟队列中的所有延迟到期元素流失到指定集中,并返回流失的延迟到期元素总数。被流失的延迟到期元素将不再存在于当前延迟队列中。
-
public int drainTo(Collection<? super E> c, int maxElements) —— 流失 —— 将当前延迟队列中最多指定数量的延迟到期元素流失到指定集中,并返回流失的延迟到期元素总数。被流失的延迟到期元素将不再存在于当前延迟队列中。
查询
-
public int size() —— 大小 —— 获取当前延迟队列的元素总数。注意是元素,而非延迟到期元素。
-
boolean isEmpty() —— 是否为空 —— 判断当前延迟队列是否为空,是则返回true;否则返回false。
-
public int remainingCapacity() —— 剩余容量 —— 获取当前延迟队列的剩余容量。由于延迟队列类是无界队列,因此该方法将永远返回Integer.MAX_VALUE。
-
public Object[] toArray() —— 转化数组 —— 获取按迭代器顺序包含当前延迟队列中所有元素的新数组。注意是元素,而非延迟到期元素。
-
public <T> T[] toArray(T[] a) —— 转化数组 —— 获取按迭代器顺序包含当前延迟队列中所有元素的泛型数组。如果参数泛型数组长度足以容纳所有元素,则令之承载所有元素后返回。并且如果参数泛型数组的长度大于当前延迟队列的元素总数,则将已承载所有元素的参数泛型数组的size索引位置设置为null,表示从当前延迟队列中承载的元素到此为止。当然,该方案只对不允许保存null元素的集有效。如果参数泛型数组的长度不足以承载所有元素,则重分配一个相同泛型且长度与当前延迟队列元素总数相同的新泛型数组以承载所有元素后返回。注意是元素,而非延迟到期元素。
迭代器
- public Iterator iterator() —— 迭代器 —— 创建可遍历当前延迟队列中元素的迭代器。注意是元素,而非延迟到期元素。
事实上,上文中只列举了大部分常用方法。由于延迟队列类是集接口的实现类,因此其也实现了其定义的所有方法,例如contains(Object o)、removeAll(Collection<?> c)、containsAll(Collection<?> c)等。但由于这些方法的执行效率不高,并且与延迟队列类的主流使用方式并不兼容/兼容性差,因此通常是不推荐使用的,有兴趣的童鞋可以去查看源码实现。
四 实现
元素
延迟队列类强制元素必须是延迟接口类型,即元素必须是延迟接口实现类对象。如此设计的原因是因为延迟队列类会调用延迟接口定义的getDelay(TimeUnit unit)方法来获取元素的剩余延迟来实现精确延迟,因此元素类必须实现该方法以返回有效的剩余延迟。除此之外,元素类还必须实现比较能力接口定义的compareTo(T o)方法以实现元素剩余延迟的比较,即元素类必须在compareTo(T o)方法中调用getDelay(TimeUnit unit)方法来比较两个元素剩余延迟的大小,这与延迟队列类底层使用优先级队列类实现有关。
元素只有在延迟到期的情况下才允许被头部移除,即只有延迟到期元素才能被头部移除,这是实现延迟队列类的核心操作。所谓的延迟到期是指元素的剩余延迟小于等于0,小于0是因为元素在延迟过期后未能被实时的头部移除。当移除者(执行移除方法的线程)到来时,该延迟到期元素可被直接头部移除;而对于尚未延迟到期的元素,则移除者必须等待其延迟到期后方可头部移除。
优先级队列
延迟队列类自身没有实现相关的数据模型,其底层使用优先级队列类实现。优先级队列类不是常规的FIFO实现,元素在内部会根据剩余延迟按小顶堆的规则进行排序。在延迟队列类的实现中,会将剩余延迟最小的元素排序在优先级队列的头部,即头元素,表示其获得了最高的优先级。这便是元素类必须在compareTo(T o)方法中实现剩余延迟比较的根本原因,因为比较是排序的基本条件。
当移除者到来时,会先获取底层优先级队列的头元素并判断。如果头元素不存在,说明延迟队列中没有可头部移除的延迟到期元素,令当前移除者进入有限/无限等待状态(具体视执行的头部移除方法的形式而定);如果头元素存在且延迟到期,说明延迟队列中存在可头部移除的延迟到期元素,当前移除者可直接将之从底层优先级队列中头部移除,并唤醒一个等待中的移除者(如果存在的话)对底层优先级队列的后续头元素进行头部移除(能否移除成功视底层优先级队列后续头元素是否延迟到期而定);而如果头元素存在但尚未延迟到期,则这是最复杂的情况,需要继续判断领导者是否存在。如果领导者存在,令当前移除者进入有限/无限等待状态;如果领导者不存在,则将当前移除者设置为领导者,并令之进入专属有限等待状态,即其等待时间与头元素的剩余延迟相同。
以上是头部移除方法“阻塞”形式的流程,其它形式的流程与之大致相同,但会根据实际需求进行调整。
领导者
所谓领导者,本质是专属等待底层优先级队列头元素延迟到期的移除者,确保头元素在延迟到期时可被实时头部移除,以实现精确延迟。延迟队列类使用了领导者 - 追随者模式的变种模式以实现最大限度的减少非必要等待。即当一个移除者成为领导者后,会专属等待底层优先级队列的头元素延迟到期,而其它移除者则会进入有限/无限等待状态,从而避免大量移除者定时等待同一个头元素的情况。当领导者超时唤醒后,此时的头元素也已经延迟到期而成为了延迟到期元素,允许被头部移除。成功执行头部移除的领导者在结束之前需要唤醒一个等待中的移除者,以期望其对底层优先级队列的后续头元素进行专属等待(如果后续头元素存在且未延迟到期),即成为新的领导者(不一定能成功,因为存在并发竞争)。
领导者无法保证在头元素延迟到期后必然将之头部移除。由于等待时间与头元素的剩余延迟相同,领导者基本可以在头元素延迟到期的同时因为超时而唤醒,从而实时头部移除已成为延迟到期元素的头元素。但这并不是必然的,由于程序运行时间消耗、CPU时间片分配、新移除者的外部竞争及其它定时移除者的内部竞争等原因,领导者无法保证必然将头元素头部移除,甚至无法保证自身是因超时而唤醒。典型的场景是:头元素延迟到期,但领导者由于上述列举的各项原因尚未因超时而唤醒。而此时恰好有新移除者成功头部移除了头元素并对领导者发送了信号,使得领导者并非因为等待超时而唤醒。头部移除头延迟到期元素失败的领导者根据头部移除方法的形式及程序的运行状态,会进行再次等待或返回null。
领导者可能在其定时等待期间被撤销。这是可以预想到的,因为领导者是专属等待底层优先级队列头元素延迟到期的线程,因此如果在等待期间底层优先级队列头元素发生改变,例如尾部插入了一个剩余延更小的元素而将之排序成为新头元素,则该领导者就失去了精确等待的作用(因为其等待时间与新头元素的剩余延迟未必相同),需要将之撤销。撤销后的领导者会唤醒一个等待中的移除者(可能是自己,因为其自身也在等待,也可能是其它移除者),以期望其对底层优先级队列的新头元素进行专属等待,即成为新的领导者(不一定能成功,因为存在并发竞争)。因此,移除者必须准备好在等待过程中获得/失去领导地位。
相关文章:

Java ~ Collection/Executor ~ DelayQueue【总结】
前言 文章 相关系列:《Java ~ Collection【目录】》(持续更新)相关系列:《Java ~ Executor【目录】》(持续更新)相关系列:《Java ~ Collection/Executor ~ DelayQueue【源码】》(学…...

前端高级面试题-安全相关
1 XSS 跨⽹站指令码(英语: Cross-site scripting ,通常简称为: XSS )是⼀种⽹站应⽤程式的安全漏洞攻击,是代码注⼊的⼀种。 它允许恶意使⽤者将程式码注⼊到⽹⻚上,其他使⽤者在观看⽹⻚时就会…...

【前缀和】560.和为 K 的子数组
Halo,这里是Ppeua。平时主要更新C,数据结构算法,Linux与ROS…感兴趣就关注我bua! 和为K的子数组 题目:示例:题解:解法一:解法二: 题目: 示例: 题解: 解法一: 暴力解法:我们很容易想到通过两个for循环去遍…...

【Docker】安全及日志管理
安全及日志管理 Docker 安全及日志管理一:Docker 容器与虚拟机的区别1. 隔离与共享2. 性能与损耗 二:Docker 存在的安全问题1.Docker 自身漏洞2.Docker 源码问题 三:Docker 架构缺陷与安全机制1. 容器之间的局域网攻击2. DDoS 攻击耗尽资源3.…...

基于x-scan扫描线的3D模型渲染算法
基于x-scan算法实现的z-buffer染色。c#语言,.net core framework 3.1运行。 模型是读取3D Max的obj模型。 x-scan算法实现: public List<Vertex3> xscan() {List<Vertex3> results new List<Vertex3>();SurfaceFormula formula g…...

LeetCode36.Valid-Sudoku<有效的数独>
题目: 思路: 这题并不难,它类似于N皇后问题。在N皇后问题中,行,列,对角线,写对角线,都不能出现连续的皇后。 本题类似,不过他是行,列,还有一个B…...

Linux中的pause函数
2023年7月29日,周六上午 函数原型 在Linux中,pause()函数用于使当前进程暂停执行,直到接收到一个信号。 #include <unistd.h>int pause(void);pause()函数不接受任何参数。 通常,pause()函数用于编写简单的信号处理程序&…...

CommonCollections6链分析
前面和CC1一样 优点是不限制jdk版本和cc的版本 先开一个ChainedTransformer 然后创LazyMap 我们顺便执行一下避免上面写错 能弹计算器 没问题 后面就是CC6不同的地方了 我们需要一个TiedMapEntry 因为需要一个类调用了get方法 在TiedMapEntry的getValue()方法中调用了get()…...

优化基于tcp,socket的ftp文件传输程序
原始程序: template_ftp_server_old.py: import socket import json import struct import os import time import pymysql.cursorssoc socket.socket(socket.AF_INET, socket.SOCK_STREAM) HOST 192.168.31.111 PORT 4101 soc.bind((HOST,PORT)) p…...

MySQL 数据库 【增删查改(二)】
目录 一、表的设计 1、一对一 2、一对多 3、多对多 二、新增 三、查询 1、聚合查询 (1)聚合函数: (2) group by 子句 (3)having 2、联合查询 (1)内连接 (2)外连接 (3)自链接 (4)…...

力扣 -- 978. 最长湍流子数组
一、题目 二、解题步骤 下面是用动态规划的思想解决这道题的过程,相信各位小伙伴都能看懂并且掌握这道经典的动规题目滴。 三、参考代码 class Solution { public:int maxTurbulenceSize(vector<int>& nums) {int nnums.size();vector<int> f(n);…...

甘特图 Dhtmlx Gantt
介绍 在一些任务计划、日程进度等场景中我们会使用到甘特图,Dhtmlx Gantt 对于甘特图的实现支持很友好,文档API介绍全面,虽然增强版的收费,但免费版的足以够用。 官网:https://docs.dhtmlx.com/gantt/ 安装dhtml gannt…...

iOS 应用上架流程详解
iOS 应用上架流程详解 欢迎来到我的博客,今天我将为大家分享 iOS 应用上架的详细流程。在这个数字化时代,移动应用已经成为了人们生活中不可或缺的一部分,而 iOS 平台的 App Store 则是开发者们发布应用的主要渠道之一。因此,了解…...

Python入门【LEGB规则、面向对象简介、面向过程和面向对象思想、面向对象是什么? 对象的进化 、类的定义、对象完整内存结构 】(十三)
👏作者简介:大家好,我是爱敲代码的小王,CSDN博客博主,Python小白 📕系列专栏:python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 📧如果文章知识点有错误…...

【消息中间件】原生PHP对接Uni H5、APP、微信小程序实时通讯消息服务
文章目录 视频演示效果前言一、分析二、全局注入MQTT连接1.引入库2.写入全局连接代码 二、PHP环境建立总结 视频演示效果 【uniapp】实现买定离手小游戏 前言 Mqtt不同环境问题太多,新手可以看下 《【MQTT】Esp32数据上传采集:最新mqtt插件(支…...

【C语言初阶】指针篇—上
目录 1. 指针是什么?2. 指针和指针类型2.1 指针-整数2.2 指针的解引用 3. 野指针3.1 野指针成因1. 指针未初始化2. 指针越界访问3. 指针指向的空间释放 3.2 如何规避野指针 1. 指针是什么? 指针是什么? 指针理解的2个要点: > 1…...

基于FasterRCNN深度学习网络的车辆检测算法matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022A 3.部分核心程序 ....................................................................... % 训练Faster R-…...

机器学习深度学习——多层感知机
👨🎓作者简介:一位即将上大四,正专攻机器学习的保研er 🌌上期文章:机器学习&&深度学习——感知机 📚订阅专栏:机器学习&&深度学习 希望文章对你们有所帮助 上一节…...

Django模型将模型注释同步到数据库
1、安装django-comment-migrate库 pip install django-comment-migrate 2、将库注册到settings.py文件中 INSTALLED_APPS [...django_comment_migrate, # 表注释... ] 3、加注释 3.1、给模型(表)加注释 在模型的class Meta中编辑 verbose_name&…...

STM32 Flash学习(二)
STM32F1的官方固件库操作FLASH的几个常用函数。这些函数和定义分布在源文件stm32f1xx_hal_flash.c/stm32f1xx_hal_flash_ex.c以及头文件stm32f1xx_hal_flash.h/stm32f1xx_hal_flash_ex.h中。 锁定解函数 对FLASH进行写操作前必须先解锁,解锁操作:在FLA…...

kotlin获取泛型集合的类型信息
通过 reified 关键字和内联函数来实现 inline fun <reified T> getClassFromList(list: List<T>): Class<T> {return T::class.java }fun main() {val list listOf("Hello", "World")val clazz getClassFromList(list)println(clazz)…...

AQS源码解析
关于 AQS,网上已经有无数的文章阐述 AQS 的使用及其源码,所以多这么一篇文章也没啥所谓,还能总结一下研究过的源码。源码解析和某某的使用,大概是互联网上 Java 文章中写得最多的主题了。 AQS AQS 是 AbstractQueuedSynchronize…...

关于在VS2017中编译Qt项目遇到的问题
关于在VS2017中编译Qt项目遇到的问题 【QT】VS打开QT项目运行不成功 error MSB6006 “cmd.exe”已退出,代码为 2。如何在VS2017里部署的Qt Designer上编辑槽函数 【QT】VS打开QT项目运行不成功 error MSB6006 “cmd.exe”已退出,代码为 2。 链接 如何在VS2017里部署的Qt Design…...

Python web实战 | 使用 Flask 实现 Web Socket 聊天室
概要 今天我们学习如何使用 Python 实现 Web Socket,并实现一个实时聊天室的功能。本文的技术栈包括 Python、Flask、Socket.IO 和 HTML/CSS/JavaScript。 什么是 Web Socket? Web Socket 是一种在单个 TCP 连接上进行全双工通信的协议。它是 HTML5 中的…...

Android10 Recovery系列(一)隐藏recovery菜单项
一 、背景 起因是遇到了一个隐藏删除recovery菜单项的需求。在寻找解决问题的时候,我经历了找到源码位置,调试修改,生效,思考是否可拓展,优化修改,符合要求的整个过程,下面简单分享一下。如果不想立即实现效果或者只想看解决方案,可以直接看总结那一个部分 二 、准备…...

选好NAS网络储存解决方案,是安全储存的关键
随着网络信息的发展,NAS也越来越受到企业的关注,NAS网络存储除了提供简单的存储服务外,还可以提供更好的数据安全性、更方便的文件共享方式。但市面上的产品种类繁多,我们该如何选择合适的产品,通过企业云盘࿰…...

AnimateDiff论文解读-基于Stable Diffusion文生图模型生成动画
文章目录 1. 摘要2. 引言3. 算法3.1 Preliminaries3.2. Personalized Animation3.3 Motion Modeling Module 4. 实验5.限制6. 结论 论文: 《AnimateDiff: Animate Your Personalized Text-to-Image Diffusion Models without Specific Tuning》 github: https://g…...

centos7安装tomcat
安装tomcat 必须依赖 JDK 环境,一定要提前装好JDK保证可以使用 一、下载安装包 到官网下载 上传到linux 服务器 二、安装tomcat 创建tomcat 文件夹 mkdir -p /usr/local/tomcat设置文件夹权限 chmod 757 tomcat将安装包上传至 新建文件夹 解压安装包 tar zx…...

【C#教程】零基础从入门到精通
今天给大家分享一套零基础从入门到精通:.NetCore/C#视频教程;这是2022年最新整理的、590G的开发教程资料。课程涵盖了.Net各方面的知识,跟着这个教程学习,就足够了。 课程分类 1、C#从基础到精通教程; 2、Winform从…...

opencv rtsp 硬件解码
讨论使用opencv的reader 硬件解码的方案有太多种,如果使用ffmpeg硬件解码是最方便的,不方便的是把解码过后的GPU 拉到 CPU 上,再使用opencv的Mat 从cpu 上上载到gpu上,是不是多了两个过程,应该是直接从GPU mat 直接去…...