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。

    领导者可能在其定时等待期间被撤销。这是可以预想到的,因为领导者是专属等待底层优先级队列头元素延迟到期的线程,因此如果在等待期间底层优先级队列头元素发生改变,例如尾部插入了一个剩余延更小的元素而将之排序成为新头元素,则该领导者就失去了精确等待的作用(因为其等待时间与新头元素的剩余延迟未必相同),需要将之撤销。撤销后的领导者会唤醒一个等待中的移除者(可能是自己,因为其自身也在等待,也可能是其它移除者),以期望其对底层优先级队列的新头元素进行专属等待,即成为新的领导者(不一定能成功,因为存在并发竞争)。因此,移除者必须准备好在等待过程中获得/失去领导地位。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/14236.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch笔记

迈向光明之路&#xff0c;必定荆棘丛生。 文章目录 一、Elasticsearch概述二、初识ES倒排索引1. 正向索引2. 倒排索引 三、ES环境搭建1. 安装单机版ES2. 安装Kibana3. 安装ik分词器3.1 在线安装ik插件3.2.离线安装ik插件&#xff08;推荐方式&#xff09;3.3 自定义词典 四、ES…

Linux上定位线上CPU飙高

【模拟场景】 写一个java main函数&#xff0c;死循环打印 System.out.println(“111111”) &#xff0c; 将其打成jar包放在linux中执行 1、通过TOP命令找到CPU耗用最厉害的那个进程的PID 2、top -H -p 进程PID 找到进程下的所有线程 可以看到 pid 为 94384的线程耗用cpu …

VUE3-04

1. 编写代码过程中的问题与解决 1.1 错误&#xff1a;cant read property of undefined(name) &#xff08;1&#xff09;首先定位错误的位置 &#xff08;2&#xff09;逐一排查问题&#xff1a;注释代码&#xff1b;debugger&#xff1b;console.log &#xff08;3&#xff0…

数组传参,指针传参

文章目录 一维数组传参二维数组传参一级指针传参二级指针传参 一维数组传参 二维数组传参 一级指针传参 二级指针传参

基于stm32单片机的直流电机速度控制——LZW

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 一、实验目的二、实验方法三、实验设计1.实验器材2.电路连接3.软件设计&#xff08;1&#xff09;实验变量&#xff08;2&#xff09;功能模块a&#xff09;电机接收信号…

Java【Spring】项目创建、存储和获取 Bean 的基本方式

文章目录 前言一、创建 Spring 项目1, 创建 Maven 项目2, 添加 Spring 依赖3, 创建启动类 二、存储 Bean 的基本方式1, 创建 Bean2, 存储 Bean 三、获取 Bean 的基本方式1, 获取上下文对象2, 获取 Bean3, 使用 Bean 总结 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的…

Python基础入门教程(上)

目录 一、你好Python 1.1、Python安装 win版 Linux版 1.2、第一个Python程序 二、Python基本语法 2.1、字面量 2.2、注释 2.3、变量 2.4、数据类型 type()函数 字符串类型的不同定义方式 2.5、数据类型转换 ​编辑 2.6、标识符 2.7、运算符 2.8、字符串扩展 …

Vue2基础五、工程化开发

零、文章目录 Vue2基础五、工程化开发 1、工程化开发和脚手架 &#xff08;1&#xff09;开发 Vue 的两种方式 核心包传统开发模式&#xff1a;基于 html / css / js 文件&#xff0c;直接引入核心包&#xff0c;开发 Vue。工程化开发模式&#xff1a;基于构建工具&#xf…

【Python】数据分析+数据挖掘——探索Pandas中的索引与数据组织

前言 在数据科学和数据分析领域&#xff0c;Pandas是一个备受喜爱的Python库。它提供了丰富的数据结构和灵活的工具&#xff0c;帮助我们高效地处理和分析数据。其中&#xff0c;索引在Pandas中扮演着关键角色&#xff0c;它是一种强大的数据组织和访问机制&#xff0c;使我们…

【Unity2D】角色动画的切换

动画状态转换 第一种方法是设置一个中间状态&#xff0c;从中间状态向其余各种状态切换&#xff0c;且各状态向其他状态需要设置参数 实现动作转移时右键点击Make Transition即可 实现动画转移需要设置条件 点击一种动画到另一种动画的线 &#xff0c;然后点击加号添加Condi…

玩转LaTeX(三)【数学公式(基础)、​矩阵、多行公式】

数学公式基础 导言区&#xff08;引包&#xff09; \usepackage{amsmath} %带星号的eqution 正文区 \begin{document}%数学公式初步 \section{简介} \LaTeX{}将排版内容分为文本模式和数学模式。文本模式用于普通文本排版&#xff0c;数学模式用于数学公式排版。 …

【LeetCode】206.反转链表

题目 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]示例 3&#xff1a; …

oracle,获取每日24*60,所有分钟数

前言&#xff1a; 为规范用户的时间录入&#xff0c;因此我们采用下拉的方式&#xff0c;让用户选择需要的时间&#xff0c;因此我们需要将一天24小时的时间拆分为类似00:00,00:01...23:00,23:01,23:59。因此我们需要生成24*601440行的下拉复选值。具体效果如下图所示。 思路 1…

【Golang 接口自动化03】 解析接口返回XML

目录 解析接口返回数据 定义结构体 解析函数&#xff1a; 测试 优化 资料获取方法 上一篇我们学习了怎么发送各种数据类型的http请求&#xff0c;这一篇我们来介绍怎么来解析接口返回的XML的数据。 解析接口返回数据 定义结构体 假设我们现在有一个接口返回的数据resp如…

❤ yarn 和npm 的使用

❤ yarn 和npm 的使用 yarn 版本1的使用 yarn 简介 Yarn是facebook发布的一款取代npm的包管理工具。 yarn特点&#xff1a; 1&#xff0c;速度超快。 Yarn 缓存了每个下载过的包&#xff0c;所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率&#xff0c;因…

【代理模式】了解篇:静态代理 动态代理~

目录 1、什么是代理模式&#xff1f; 2、静态代理 3、动态代理 3.1 JDK动态代理类 3.2 CGLIB动态代理类 4、JDK动态代理和CGLIB动态代理的区别&#xff1f; 1、什么是代理模式&#xff1f; 定义&#xff1a; 代理模式就是为其他对象提供一种代理以控制这个对象的访问。在某…

华为nat64配置

1.前期环境准备 环境拓扑 拓扑分为两个区域,左边为trust区域,使用IPv4地址互访,右边为untrust区域,使用IPv6地址互访 2.接口地址配置 pc1地址配置 pc2地址配置 FW接口配置 (1)首先进入防火墙配置界面 注:防火墙初始账号密码为user:admin,pwd:Admin@123,进入之后…

8.docker仓库

文章目录 Docker仓库本地私有仓库Docker HarborDocker harbor部署访问页面创建用户下载私有仓库镜像harbor同步 Docker仓库 本地私有仓库 ##先下载 registry 镜像docker pull registry##修改配置文件&#xff0c;在 daemon.json 文件中添加私有镜像仓库地址vim /etc/dock…

SQL-每日一题【1070. 产品销售分析 III】

题目 销售表 Sales&#xff1a; 产品表 Product&#xff1a; 编写一个 SQL 查询&#xff0c;选出每个销售产品 第一年 销售的 产品 id、年份、数量 和 价格。 结果表中的条目可以按 任意顺序 排列。 查询结果格式如下例所示&#xff1a; 示例 1&#xff1a; 解题思路 前置知…

Python爬虫的urlib的学习(学习于b站尚硅谷)

目录 一、页面结构的介绍  1.学习目标  2.为什么要了解页面&#xff08;html&#xff09;  3. html中的标签&#xff08;仅介绍了含表格、无序列表、有序列表、超链接&#xff09;  4.本节的演示 二、Urllib  1.什么是互联网爬虫&#xff1f;  2.爬虫核心  3.爬虫…