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

【JUC系列-13】深入理解DelayQueue延迟队列的底层原理

JUC系列整体栏目


内容链接地址
【一】深入理解JMM内存模型的底层实现原理https://zhenghuisheng.blog.csdn.net/article/details/132400429
【二】深入理解CAS底层原理和基本使用https://blog.csdn.net/zhenghuishengq/article/details/132478786
【三】熟练掌握Atomic原子系列基本使用https://blog.csdn.net/zhenghuishengq/article/details/132543379
【四】精通Synchronized底层的实现原理https://blog.csdn.net/zhenghuishengq/article/details/132740980
【五】通过源码分析AQS和ReentrantLock的底层原理https://blog.csdn.net/zhenghuishengq/article/details/132857564
【六】深入理解Semaphore底层原理和基本使用https://blog.csdn.net/zhenghuishengq/article/details/132908068
【七】深入理解CountDownLatch底层原理和基本使用https://blog.csdn.net/zhenghuishengq/article/details/133343440
【八】深入理解CyclicBarrier底层原理和基本使用https://blog.csdn.net/zhenghuishengq/article/details/133378623
【九】深入理解ReentrantReadWriteLock 读写锁的底层实现https://blog.csdn.net/zhenghuishengq/article/details/133629550
【十】深入理解ArrayBlockingQueue的基本使用和底层实现https://blog.csdn.net/zhenghuishengq/article/details/133692023
【十一】深入理解LinkedBlockingQueue的基本使用和底层实现https://blog.csdn.net/zhenghuishengq/article/details/133723652
【十二】深入理解PriorityQueue的基本使用和底层实现https://blog.csdn.net/zhenghuishengq/article/details/133788655
【十三】深入理解DelayQueue的基本使用和底层实现https://blog.csdn.net/zhenghuishengq/article/details/133820599

深入理解DelayQueue延迟队列的底层原理

  • 一,深入理解DelayQueue延迟队列
    • 1,DelayQueue的基本使用
    • 2,DelayQueue的底层源码分析
      • 2.1,DelayQueue类属性
      • 2.2,入队offer方法
      • 2.3,出队take方法
    • 3,总结

一,深入理解DelayQueue延迟队列

延时队列,顾名思义,就是可以实现在一段时间之后在执行这个任务。在分布式场景下可能会更加的选择使用MQ来完成这些操作,但是在单JVM进程中,或者在mq挂了的兜底方案中,会考虑使用这个DelayQueue来完成这个延时任务的。如一些订单超时未支付,任务超时管理,短信异步通知等情况,就可以使用这个延时队列来完成了。

在了解这个DelayQueue延迟队列之前,需要先熟悉上一篇PriorityQueue的基本使用和底层原理,因为这个延迟队列的底层的数据结构,就是通过这个优先级队列来实现的

class DelayQueue<E extends Delayed> extends AbstractQueue<E> implements BlockingQueue<E>{//组合了一个优先级队列private final PriorityQueue<E> q = new PriorityQueue<E>();
}

由于这个优先级队列采用的是二叉堆的数据结构,并且采用的是小顶堆的数据结构,因此很容易猜出这个DelayQueue的底层原理了,就是假设5个延时任务,会将最近到期的这个任务排在阻塞队列的前面,因此在出队的时候,就可以保证先过期的先出队。

由于底层是通过这个PriorityQueue的优先级队列实现的,因此这个DelayQueue也是一个无界的阻塞队列,在使用这个延迟队列时,需要实现一个Delayed 的接口。总而言之就是:不保证先进先出,下一个即将过期的任务会排到队列的最前面

1,DelayQueue的基本使用

由于在实际开发中,会有这种订单超时的场景,因此这里主要是模拟一个订单的超时任务,来体验一下这个DelayQueue的基本使用

首先创建一个实现了Delayed接口的OrderDelay订单延时类,Delayed也是Comparable类的一个具体实现

/*** Delayed的具体的方法实现* @Author: zhenghuisheng* @Date: 2023/10/14 0:32*/
@Data
public class OrderDelay implements Delayed {//需要延迟的时间private long delayTime;//订单idprivate Integer orderId;//商品名称private String productName;//构造方法public OrderDelay(long delayTime,Integer orderId,String productName){//需要延迟的 时间 + 当前系统的时间this.delayTime = delayTime + System.currentTimeMillis();this.orderId = orderId;this.productName = productName;}//获取剩余的延时时间@Overridepublic long getDelay(TimeUnit unit) {//到达时间 - 剩余时间long residueTime = this.delayTime - System.currentTimeMillis();return unit.convert(residueTime,TimeUnit.MILLISECONDS);}//实现这个比较器方法@Overridepublic int compareTo(Delayed o) {OrderDelay orderDelay = (OrderDelay)o;return orderDelay.delayTime > this.delayTime ? - 1 : 1;}
}

随后创建一个生产者Producer线程任务类,用于将为支付的订单加入到这个延时队列中

@Data
public class Producer implements Runnable {//全局的阻塞队列private DelayQueue queue;//延迟队列订单类对象private  OrderDelay orderDelay;public Producer(DelayQueue queue,OrderDelay orderDelay){this.queue = queue;this.orderDelay = orderDelay;}//添加文件@Overridepublic void run() {try {queue.put(orderDelay);	//加入阻塞队列System.out.println(orderDelay.getProductName() + "加入完毕...");} catch (Exception e) {e.printStackTrace();}}
}

随后创建一个消费者Consumer线程任务类,用于取出即将过期的订单任务

/*** 消费者线程* @Author: zhenghuisheng* @Date: 2023/10/8 20:21*/
@Data
public class Consumer implements Runnable {private DelayQueue queue;public Consumer(DelayQueue delayQueue){this.queue = delayQueue;}@Overridepublic void run() {try {System.out.println(queue.take());} catch (InterruptedException e) {e.printStackTrace();}}
}

然后再创建一个线程池的工具类,用于更好的监控和管理线程

/*** 线程池工具* @author zhenghuisheng* @date : 2023/3/22*/
public class ThreadPoolUtil {/*** io密集型:最大核心线程数为2N,可以给cpu更好的轮换,*           核心线程数不超过2N即可,可以适当留点空间* cpu密集型:最大核心线程数为N或者N+1,N可以充分利用cpu资源,N加1是为了防止缺页造成cpu空闲,*           核心线程数不超过N+1即可* 使用线程池的时机:1,单个任务处理时间比较短 2,需要处理的任务数量很大*/public static synchronized ThreadPoolExecutor getThreadPool() {if (pool == null) {//获取当前机器的cpuint cpuNum = Runtime.getRuntime().availableProcessors();log.info("当前机器的cpu的个数为:" + cpuNum);int maximumPoolSize = cpuNum * 2 ;pool = new ThreadPoolExecutor(maximumPoolSize - 2,maximumPoolSize,5L,   //5sTimeUnit.SECONDS,new LinkedBlockingQueue<>(),  //数组有界队列Executors.defaultThreadFactory(), //默认的线程工厂new ThreadPoolExecutor.AbortPolicy());  //直接抛异常,默认异常}return pool;}
}

最后创建一个有Main方法的测试类,用于对这个DelayQueue进行测试

/*** @Author: zhenghuisheng* @Date: 2023/10/14 1:41*/
public class DelayQueueDemo {//创建一个线程池static ThreadPoolExecutor pool = ThreadPoolUtil.getThreadPool();//创建一个全局的延迟队列static DelayQueue<OrderDelay> delayQueue = new DelayQueue();public static void main(String[] args) throws Exception {//生产者创建任务for (int i = 7; i > 2; i--) {OrderDelay orderDelay = new OrderDelay(i * 1000, i, "id_" + i);//创建生产者线程Producer producerTask = new Producer(delayQueue, orderDelay);//提交到线程池pool.execute(producerTask);}Thread.sleep(50);System.out.println("====生产者线程创建完毕====");//创建消费者线程for (int i = 0; i < 5; i++) {Consumer consumerTask = new Consumer(delayQueue);pool.execute(consumerTask);}}
}

最后看执行结果,先进来但是延迟时间长,所以后出去

id_7加入完毕…
id_5加入完毕…
id_6加入完毕…
id_4加入完毕…
id_3加入完毕…
生产者线程创建完毕
OrderDelay(delayTime=1697221724133, orderId=3, productName=id_3)
OrderDelay(delayTime=1697221725133, orderId=4, productName=id_4)
OrderDelay(delayTime=1697221726132, orderId=5, productName=id_5)
OrderDelay(delayTime=1697221727132, orderId=6, productName=id_6)
OrderDelay(delayTime=1697221728129, orderId=7, productName=id_7)

2,DelayQueue的底层源码分析

2.1,DelayQueue类属性

首先查看这个 DelayQueue 类,也是继承了这个抽象类,也是实现了这个BlockingQueue

class DelayQueue<E extends Delayed> extends AbstractQueue<E> implements BlockingQueue<E>

在这个类中首先最重要的就是这个PriorityQueue优先级队列,说明这个延迟队列的底层是通过这个优先级队列实现的

//组合了一个优先级队列
private final PriorityQueue<E> q = new PriorityQueue<E>();

随后就是一把互斥锁加一个条件队列组成,互斥锁就是offer方法和take方法的互斥,然后这个条件队列是在队列为空时存储这个线程结点的

private final transient ReentrantLock lock = new ReentrantLock();
private final Condition available = lock.newCondition();

还有一个重要的属性,就是一个leader的线程标记,用对记录队头的线程,谁最早过期就记录谁

private Thread leader = null;

最后来看看该类的构造方法,里面是空的,因为里面的offer和take主要是操作这个PriorityQueue类

public DelayQueue() {}

2.2,入队offer方法

接下来直接看这个类的offer方法的具体实现,这下面的逻辑是比较简单的,就是先入队,如果是第一个元素入队,那么回去唤醒条件队列中被阻塞的结点,因为这些结点是队列为空而将线程阻塞的,现在队列已经不为空了

public boolean offer(E e) {final ReentrantLock lock = this.lock;	//获取这把互斥锁lock.lock();		//加锁try {q.offer(e);		//线程入队if (q.peek() == e) {	//如果堆顶为当前元素,表示第一个元素入队leader = null;available.signal();	//那么就会去唤醒因对列为空而被阻塞的线程结点}return true;} finally {lock.unlock();	//解锁}
}

随后依旧是进入上面的offer方法,做一个具体的入队操作,这里需要结合PriorityQueue的属性来看,首先会判断这个数组是否达到设置的最大值或者扩容后的最大值,如果是,则继续扩容

public boolean offer(E e) {if (e == null)	throw new NullPointerException(); 	//结点为空modCount++;	int i = size;if (i >= queue.length)	//达到最大值grow(i + 1);	//扩容size = i + 1;		if (i == 0) queue[0] = e;	//队列为空则直接加入堆顶else	siftUp(i, e);	//否则上浮,堆算法return true;
}

数组扩容的方法如下,先做一个扩容操作,并且最后创建一个新数组,将旧值copy到新数组中,随后将新数组返回假设此时的容量小于64,则扩大原来的容量+2,如果大于64,则扩大原来的容量一倍。

就是说假设此时容量为16,那么第一次扩容就是 16+16+2为34,第二次扩容为34 + 34 + 2为70,第三次扩容为70 + 70*2 = 210

private void grow(int minCapacity) {int oldCapacity = queue.length;// Double size if small; else grow by 50%int newCapacity = oldCapacity + ((oldCapacity < 64) ?(oldCapacity + 2) :(oldCapacity >> 1));// overflow-conscious codeif (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);queue = Arrays.copyOf(queue, newCapacity);
}

如果此时不是第一个结点入队,那么就会调用这个 siftUp 方法,如果有自定义实现的比较器,则用自定义的,否则则直接使用内部默认的比较器

private void siftUp(int k, E x) {if (comparator != null)siftUpUsingComparator(k, x);elsesiftUpComparable(k, x);
}

接着直接来看内部默认实现的这个上浮的方法吧,就是一个小顶堆的入队操作

private static <T> void siftUpComparable(int k, T x, Object[] array) {Comparable<? super T> key = (Comparable<? super T>) x;	//创建一个比较构造器while (k > 0) {	//队列的元素值int parent = (k - 1) >>> 1;	//获取当前结点的父节点的索引,左移一位即可Object e = array[parent];	//根据索引下标取值if (key.compareTo((T) e) >= 0)	//比较和交换,如果当前值大于父节点则不动break;array[k] = e;	//如果当前结点的值小于父结点,则将当前结点改成父结点的值(默认使用的是小顶堆)k = parent;		//k在这个while循环下一定会等于0,因此会走最下面的赋值,就是不断地通过while循环将最小的交换到最上面}array[k] = key;	//如果队列的长度为0,则直接将堆顶元素赋值
}

在成功入队之后,最后会调用这个unlock方法,用于解锁,并且唤醒被阻塞的结点

lock.unlock();

2.3,出队take方法

在结点入队之后,那么接下来就看这个结点出队的方法,出队方法相对来说是稍微多一点的。首先出队第一个头结点,如果已经过期则直接出队,否者获取这个即将过期的时间延迟阻塞,即阻塞到到一定的时间主动唤醒,最后执行这个任务,会在这个for自旋中,可以保证所有的结点出队。并且通过一个临时变量 leader,只需获取最早过期的结点进行阻塞,从而不需关心比该结点更晚过期的结点,从而减少阻塞的数量。

public E take() throws InterruptedException {final ReentrantLock lock = this.lock;lock.lockInterruptibly();try {for (;;) {	//自旋E first = q.peek();	//头结点出队if (first == null)	//如果队列为空available.await();	//则加入条件队列阻塞else {//获取头结点的过期时间long delay = first.getDelay(NANOSECONDS);	//头结点的过期时间小于0,说明已经过期。则直接出队if (delay <= 0)	return q.poll();first = null; // don't retain ref while waiting//特别说明,这个leader就是用于记录最早过期的那个线程if (leader != null)	//如果已经存在记录的最近过期的结点available.await();	//则阻塞else {Thread thisThread = Thread.currentThread();leader = thisThread;try {//延时阻塞,阻塞到一定时间主动唤醒available.awaitNanos(delay);} finally {if (leader == thisThread)leader = null;}}}}} finally {if (leader == null && q.peek() != null)	available.signal();		//优化,主动唤醒lock.unlock();	//解锁}
}

最后会通过unlock进行一个解锁操作。

lock.unlock();

3,总结

延迟队列的底层是通过这个优先级队列来实现的,越早过期的结点越先出队,内部也是采用ReentrantLock+条件队列来实现安全问题以及性能问题。延迟队列的结构也是无界队列形成的数组,在入队的结点元素需要时Delayed类的具体实现。

相关文章:

【JUC系列-13】深入理解DelayQueue延迟队列的底层原理

JUC系列整体栏目 内容链接地址【一】深入理解JMM内存模型的底层实现原理https://zhenghuisheng.blog.csdn.net/article/details/132400429【二】深入理解CAS底层原理和基本使用https://blog.csdn.net/zhenghuishengq/article/details/132478786【三】熟练掌握Atomic原子系列基本…...

Leetcode---365周赛

题目列表 2873. 有序三元组中的最大值 I 2874. 有序三元组中的最大值 II 2875. 无限数组的最短子数组 2876. 有向图访问计数 一、有序三元组中的最大值I 看一眼该题的数据范围&#xff0c;直接三层for循环暴力枚举&#xff0c;时间复杂度O(n^3)&#xff0c;代码如下 class…...

Java使用opencv实现人脸识别、人脸比对

1. opencv概述 OpenCV是一个开源的计算机视觉库&#xff0c;它提供了一系列丰富的图像处理和计算机视觉算法&#xff0c;包括图像读取、显示、滤波、特征检测、目标跟踪等功能。 opencv官网&#xff1a;https://opencv.org/ opencv官网文档&#xff1a;https://docs.opencv.or…...

Redis HyperLogLog的使用

Redis HyperLogLog知识总结 一、简介二、使用 一、简介 Redis HyperLogLog是一种数据结构&#xff0c;用于高效地计算基数&#xff08;集合中唯一元素的数量&#xff09;。它的主要作用是用于在内存中高效地存储和计算大量数据的基数&#xff0c;而无需完全存储所有的数据。Hy…...

Apisix-Ingress服务发现详解

apisix Apache APISIX 是一个基于微服务 API 网关&#xff0c;其不仅可以处理南北向的流量&#xff0c;也可以处理东西向的流量即服务之间的流量。Apache APISIX 集成了控制面板和数据面&#xff0c;与其他 API 网关相比&#xff0c;Apache APISIX 的上游、路由、插件全是动态的…...

spring6-事务

文章目录 1、JdbcTemplate1.1、简介1.2、准备工作1.3、实现CURD①装配 JdbcTemplate②测试增删改功能③查询数据返回对象④查询数据返回list集合⑤查询返回单个的值 2、声明式事务概念2.1、事务基本概念①什么是事务②事务的特性 2.2、编程式事务2.3、声明式事务 3、基于注解的…...

JavaFx学习问题2--音频、视频播放失败情况

文章目录 一、路径注意事项&#xff1a;① 用相对路径的时候别忘了前面的斜杠② uri问题 二、播放不了的问题① 获取的媒体文件路径本身就是不对的② 必须是uri③ 特殊情况 额外收获: 一、路径注意事项&#xff1a; 完整代码如下: import javafx.application.Application; im…...

第55节—— redux-toolkit中的createReducer——了解

一、概念 当我们使用 Redux 开发应用程序时&#xff0c;一个非常重要的概念就是 reducer。一个 reducer 是一个纯函数&#xff0c;它接受先前的状态和一个动作&#xff0c;然后返回一个新状态。每个动作都会引起状态的变化&#xff0c;从而使应用程序状态管理更加清晰和可控。…...

JUC并发编程——JUC并发编程概述及Lock锁(重点)(基于狂神说的学习笔记)

基于bilibili狂神说JUC并发编程视频所做笔记 概述 什么是JUC JUC时java.util工具包中的三个包的简称 java.util.concurrent java.util.concurrent.atomic java.util.concurrent.locks 业务&#xff1a;普通的线程代码中&#xff0c;我们常使用Runnable接口 但Runnable没有返…...

深入了解 Java 中的时间信息定义、转换、比较和操作

1. 简介 在过去的传统Java日期处理中&#xff0c;经常面临着一些问题。比如&#xff0c;java.util.Date和java.util.Calendar在表示日期和时间时存在着一些奇怪的行为&#xff0c;如月份从0开始计数、对日期进行格式化的方式繁琐不直观等。这些问题给开发带来了一定的困扰。 …...

2023年中国智能矿山发展历程及趋势分析:智能矿山健康有序发展[图]

智能矿山系统对矿山生产提质增效的效果已经开始显现&#xff1a;对不合规、有风险的行动进行及时预警&#xff0c;减少安全事故发生概率&#xff0c;避免因停产整顿产生的巨额亏损&#xff1b;精细化管理整个生产流程&#xff0c;避免过往传统粗放的流程导致的浪费&#xff0c;…...

acwing算法基础之基础算法--整数离散化算法

目录 1 知识点2 模板 1 知识点 整个范围很大&#xff0c;但存在的数据点很少。比如从 − 1 0 9 -10^9 −109到 1 0 9 10^9 109&#xff0c;但总共只有 1 0 6 10^6 106个数。 可以采用离散化的思想来做&#xff0c;即将离散的大数值映射成连续的小数值&#xff08;一般是 1 , …...

基于SSM框架的安全教育平台

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…...

Kafka生产者使用案例

1.生产者发送消息的过程 首先介绍一下 Kafka 生产者发送消息的过程&#xff1a; 1)Kafka 会将发送消息包装为 ProducerRecord 对象&#xff0c; ProducerRecord 对象包含了目标主题和要发送的内容&#xff0c;同时还可以指定键和分区。在发送 ProducerRecord 对象前&#xff0c…...

EasyX图形库实现贪吃蛇游戏

⭐大家好&#xff0c;我是Dark Falme Masker,学习了动画制作及键盘交互之后&#xff0c;我们就可以开动利用图形库写一个简单的贪吃蛇小游戏&#xff0c;增加学习乐趣。 ⭐专栏&#xff1a;EasyX部分小游戏实现详细讲解 最终效果如下 首先包含头文件 #include<stdio.h> #…...

利用 Amazon CodeWhisperer 激发孩子的编程兴趣

我是一个程序员&#xff0c;也是一个父亲。工作之余我会经常和儿子聊他们小学信息技术课学习的 Scratch 和 Kitten 这两款图形化的少儿编程工具。 我儿子有一次指着书房里显示器上显示的 Visual Studio Code 问我&#xff0c;“为什么我们上课用的开发界面&#xff0c;和爸爸你…...

2023年中国分子筛稀土催化材料竞争格局及行业市场规模分析[图]

稀土催化材料能够起到提高催化剂热稳定性、催化剂活性、催化剂储氧能力&#xff0c;以及减少贵金属活性组分用量等作用&#xff0c;广泛应用于石油化工、汽车尾气净化、工业废气和人居环境净化、燃料电池等领域。 2015-2023年中国稀土催化材料规模及预测 资料来源&#xff1a;…...

vue3插件——vue-web-screen-shot——实现页面截图功能

最近在看前同事发我的vue3框架时&#xff0c;发现他们有个功能是要实现页面截图功能。 vue3插件——vue-web-screen-shot——实现页面截图功能 效果图如下&#xff1a;1.操作步骤1.1在项目中添加vvue-web-screen-shot组件1.2在项目入口文件导入组件——main.ts1.3在需要使用的页…...

简单总结Centos7安装Tomcat10.0版本

文章目录 前言JDK8安装部署Tomcat 前言 注意jdk与tomcat的兼容问题&#xff0c;其他的只要正确操作一般问题不大 Tomcat 是由 Apache 开发的一个 Servlet 容器&#xff0c;实现了对 Servlet 和 JSP 的支持&#xff0c;并提供了作为Web服务器的一些特有功能&#xff0c;如Tomca…...

ffmpeg中AVCodecContext和AVCodec的关系分析

怎么理解AVCodecContext和AVCodec的关系 AVCodecContext和AVCodec是FFmpeg库中两个相关的结构体&#xff0c;它们在音视频编解码中扮演着不同的角色。 AVCodecContext&#xff1a;是编解码器上下文结构体&#xff0c;用于存储音视频编解码器的参数和状态信息。它包含了进行音视…...

2023年中国门把手产量、销量及市场规模分析[图]

门把手行业是指专门从事门把手的设计、制造、销售和安装等相关业务的行业。门把手是门窗装饰硬件的一种&#xff0c;用于开启和关闭门窗&#xff0c;同时也具有装饰和美化门窗的作用。 门把手行业分类 资料来源&#xff1a;共研产业咨询&#xff08;共研网&#xff09; 随着消…...

HTML 核心技术点基础详细解析以及综合小案例

核心技术点 网页组成 排版标签 多媒体标签及属性 综合案例一 - 个人简介 综合案例二 - Vue 简介 02-标签语法 HTML 超文本标记语言——HyperText Markup Language。 超文本&#xff1a;链接 标记&#xff1a;标签&#xff0c;带尖括号的文本 标签结构 标签要成…...

BAT学习——批处理脚本(也称为BAT文件)常用语法元素与命令

批处理脚本&#xff08;也称为BAT文件&#xff09;使用Windows的批处理语言编写&#xff0c;它具有一些常用的语法元素和命令。以下是一些BAT编程的常用语法元素和命令&#xff1a; 命令行命令&#xff1a; 批处理脚本通常包含一系列Windows命令&#xff0c;例如echo&#xff0…...

AMD AFMF不但能用在游戏,也适用于视频

近期AMD发布了AMD Software Adrenalin Edition预览版驱动程序&#xff0c;增加了对平滑移动帧&#xff08;AMD Fluid Motion Frames&#xff0c;AFMF&#xff09;功能的支持&#xff0c;也就是AMD的“帧生成”技术&#xff0c;与DLSS 3类似&#xff0c;作为FidelityFX Super Re…...

CSS 常用样式浮动属性

一、概述 CSS 中&#xff0c;浮动属性的作用是让元素向左或向右浮动&#xff0c;使其他元素围绕它排布&#xff0c;常用的浮动属性有以下几种&#xff1a; float: left; 使元素向左浮动&#xff0c;其他元素从右侧包围它。 float: right; 使元素向右浮动&#xff0c;其他元素…...

Linux引导故障排除:从问题到解决方案的详细指南

1 BIOS初始化 通电->对硬件检测->初始化硬件时钟 2 磁盘引导及其修复 2.1 磁盘引导故障 磁盘主引导记录&#xff08;MBR&#xff09;是在0磁道1扇区位置&#xff0c;446字节。 MBR作用&#xff1a;记录grub2引导文件的位置 2.2 修复 步骤&#xff1a;1、光盘进…...

【vim 学习系列文章 6 -- vim 如何从上次退出的位置打开文件】

文章目录 1.1 vim 如何从上次退出的位置打开文件1.2 autogroup 命令学习1.2.1 augroup 基本语法 1.3 vim call 命令详细介绍 1.1 vim 如何从上次退出的位置打开文件 假设我打开了文件 test.c&#xff0c;然后我向下滚动到第 50 行&#xff0c;然后我做了一些修改并关闭了文件。…...

怎样学习C#上位机编程?

怎样学习C#上位机编程&#xff1f; 00001. 掌握C#编程和.NET框架基础。 00002. 学WinForm应用开发&#xff0c;了解控件使用和事件编程。 00003. 熟悉基本数据结构和算法&#xff0c;如链表、栈、队列。 00004. 理解串口通信协议和方法&#xff0c;用于与硬件交互。 00005…...

【算法-动态规划】两个字符串的删除操作-力扣 583

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…...

【06】基础知识:typescript中的泛型

一、泛型的定义 在软件开发中&#xff0c;我们不仅要创建一致的定义良好的API&#xff0c;同时也要考虑可重用性。 组件不仅能支持当前数据类型&#xff0c;同时也能支持未来的数据类型&#xff0c;这在创建大型系统时提供了十分灵活的功能。 在像 C# 和 Java 这样的语言中&…...

flutter 绘制原理探究

文章目录 Widget1、简介2、源码分析Element1、简介2、源码分析RenderObjectWidget 渲染过程总结思考Flutter 的核心设计思想便是“一切皆 Widget”,Widget 是 Flutter 功能的抽象描述,是视图的配置信息,同样也是数据的映射,是 Flutter 开发框架中最基本的概念。 在 Flutter…...

[Java]SPI扩展功能

一、什么是SPI Java SPI&#xff08;Service Provider Interface&#xff09;是Java官方提供的一种服务发现机制。 它允许在运行时动态地加载实现特定接口的类&#xff0c;而不需要在代码中显式地指定该类&#xff0c;从而实现解耦和灵活性。 二、实现原理 基于 Java 类加载…...

机器人命令表设计

演算命令 CLEAR 将数据 1 上被指定的编号以后的变数的内容&#xff0c;以及数据 2 上仅被指定的个数都清除至 0。 INC 在被指定的变数内容上加上 1。 DEC 在被指定的变数内容上减掉 1。 SET 在数据 1 上设定数据 2。 ADD 将数据 1 和数据 2 相加&#xff0c;得出的结果保存在数…...

STM32--WDG看门狗

文章目录 WDG简介IWDGIWDG的超时计算WWDGWWDG超时和窗口值设定独立看门狗工程WWDG工程 WDG简介 WDG看门狗&#xff08;Watchdog Timer&#xff09;是一种常见的硬件设备&#xff0c;在STM32F10系列中&#xff0c;有两种看门狗&#xff0c;分别是独立看门狗和窗口看门狗&#x…...

(※)力扣刷题-字符串-实现 strStr()(KMP算法)

28 实现 strStr() 实现 strStr() 函数。 给定一个 haystack 字符串和一个 needle 字符串&#xff0c;在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在&#xff0c;则返回 -1。 示例 1: 输入: haystack “hello”, needle “ll” 输出: 2 示例…...

Redis 集群 Redis 事务 Redis 流水线 Redis 发布订阅 Redis Lua脚本操作

Redis 集群 & Redis 事务 & Redis 流水线 & Redis 发布订阅 Redis 集群linux安装redis主从配置查看当前实例主从信息 Redis Sentinelsentinel Redis Cluster Redis 事务Redis 流水线Redis 发布订阅Redis Lua脚本操作 Redis 集群 linux安装redis 下载安装包&#…...

【算法与数据结构】--常见数据结构--栈和队列

一、栈 栈&#xff08;Stack&#xff09; 是一种基本的数据结构&#xff0c;具有后进先出&#xff08;LIFO&#xff09;的特性&#xff0c;类似于现实生活中的一叠盘子。栈用于存储一组元素&#xff0c;但只允许在栈顶进行插入&#xff08;入栈&#xff09;和删除&#xff08;…...

Linux shell编程学习笔记11:关系运算

Linux Shell 脚本编程和其他编程语言一样&#xff0c;支持算数、关系、布尔、字符串、文件测试等多种运算。前面几节我们研究了 Linux shell编程 中的 字符串运算 和 算术运算&#xff0c;今天我们来研究 Linux shell编程中的的关系运算。 一、关系运算符功能说明 运算符说明…...

JS标准库

学习一门编程语言不仅是掌握其语法。同等重要的是学习其标准库&#xff0c;从而熟练掌握语言本身提供的所有工具。 1 定型数组 js常规数组与C和Java等较低级语言的数组类型还是有很大区别。ES6新增了定型数组&#xff0c;与这些语言的低级数组非常接近。 定型数组严格来说并…...

Android 12.0 hal层添加自定义hal模块功能实现

1. 前言 在12.0的系统rom定制化开发中,在 对hal模块进行开发时,需要通过添加自定义的hal模块来实现某些功能时,就需要添加hal模块的相关功能,接下来就来实现一个案例来供参考 接下来就来具体实现这个功能 2.hal层添加自定义hal模块功能实现的核心类 hardware\interfaces…...

如何理解vue声明式渲染

Vue.js中的声明式渲染是一种用来描述用户界面的方式&#xff0c;它强调“声明”应该如何渲染页面&#xff0c;而不需要关心底层的DOM操作。这与传统的命令式渲染方式&#xff0c;即手动控制DOM元素的创建、更新和销毁&#xff0c;形成了鲜明的对比。 理解Vue的声明式渲染的关键…...

【已解决】Vue全局引入scss 个别页面不生效 / 不自动引入全局样式

项目里配置了全局样式的引入&#xff0c;今天新建了 demo 页面去修改 element 的样式&#xff0c;发现全局的样式没有引入进来。 问题原因 在此页面 没有任何样式导致的 项目在编译的时候&#xff0c;会把 .vue 文件的样式抽离到单独的 css 文件中。 当该页面没有css代码的时…...

MySQL之双主双从读写分离

一个主机 Master1 用于处理所有写请求&#xff0c;它的从机 Slave1 和另一台主机 Master2 还有它的从 机 Slave2 负责所有读请求。当 Master1 主机宕机后&#xff0c; Master2 主机负责写请求&#xff0c; Master1 、 Master2 互为备机。架构图如下 : 准备 我们…...

使用eBPF加速阿里云服务网格ASM

背景 随着云原生应用架构的快速发展&#xff0c;微服务架构已经成为了构建现代应用的主要方式之一。而在微服务架构中&#xff0c;服务间的通信变得至关重要。为了实现弹性和可伸缩性&#xff0c;许多组织开始采用服务网格技术来管理服务之间的通信。 Istio作为目前最受欢迎的…...

大型数据集处理之道:深入了解Hadoop及MapReduce原理

在大数据时代&#xff0c;处理海量数据是一项巨大挑战。而Hadoop作为一个开源的分布式计算框架&#xff0c;以其强大的处理能力和可靠性而备受推崇。本文将介绍Hadoop及MapReduce原理&#xff0c;帮助您全面了解大型数据集处理的核心技术。 Hadoop简介 Hadoop是一个基于Google…...

LCR 095. 最长公共子序列(C语言+动态规划)

1. 题目 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删除某些字符&#xff08…...

程序员不写注释:探讨与反思

一、为什么程序员不写注释 当程序员选择不写注释时&#xff0c;通常有一系列常见原因&#xff0c;这些原因可以影响他们的决策和行为。同时&#xff0c;这个决策可能会带来多方面的影响和后果。以下是详细阐述为什么程序员不写注释的常见原因以及这种决策可能导致的影响和后果…...

《论文阅读:Dataset Condensation with Distribution Matching》

点进去这篇文章的开源地址&#xff0c;才发现这篇文章和DC DSA居然是一个作者&#xff0c;数据浓缩写了三篇论文&#xff0c;第一篇梯度匹配&#xff0c;第二篇数据增强后梯度匹配&#xff0c;第三篇匹配数据分布。DC是匹配浓缩数据和原始数据训练一次后的梯度差&#xff0c;DS…...

免费chatGPT工具

发现很多人还是找不到好用的chatGPT工具&#xff0c;这里分享一个邮箱注册即可免费试用。 PromptsZone - 一体化人工智能平台使用 PromptsZone 与 ChatGPT、Claude、AI21 Labs、Google Bard 聊天&#xff0c;并使用 DALL-E、Stable Diffusion 和 Google Imagegen 创建图像&…...

数据分析基础:数据可视化+数据分析报告

数据分析是指通过对大量数据进行收集、整理、处理和分析&#xff0c;以发现其中的模式、趋势和关联&#xff0c;并从中提取有价值的信息和知识。 数据可视化和数据分析报告是数据分析过程中非常重要的两个环节&#xff0c;它们帮助将数据转化为易于理解和传达的形式&#xff0…...