多线程与并发编程 面试专题

多线程与并发编程 面试专题
- 线程的基础概念
- 基础概念
- 线程的创建
- 线程的状态
- 线程的终止方式
- start 与 run 区别
- 线程的常用方法
- 锁
- 锁的分类
- 深入synchronized
- 深入ReentrantLock
- 死锁问题
- 阻塞队列
- 线程池
线程的基础概念
基础概念
- 进程与线程
- 进程:指
运行中的程序。 比如我们使用钉钉,浏览器,需要启动这个程序,操作系统会给这个程序分配一定的资源(占用内存资源)。 - 线程:
CPU调度的基本单位,每个线程执行的都是某一个进程的代码的某个片段。
- 进程:指
- 多线程
- 单个进程中同时运行多个线程。
- 多线程的不低是为了提高CPU的利用率。可以通过避免一些网络IO或者磁盘IO等需要等待的操作,让CPU去调度其他线程。这样可以大幅度的提升程序的效率,提高用户的体验。
- 多线程的局限
- 如果线程数量特别多,CPU在切换线程上下文时,会额外造成很大的消耗。
- 任务的拆分需要依赖业务场景,有一些异构化的任务,很难对任务拆分,还有很多业务并不是多线程处理更好。
- 线程安全问题:虽然多线程带来了一定的性能提升,但是再做一些操作时,多线程如果操作临界资源,可能会发生一些数据不一致的安全问题,甚至涉及到锁操作时,会造成死锁问题。
- 串行、并行、并发
- 串行:任务按严格顺序执行,前一个任务完成后再开始下一个。
- 并行:多个任务真正同时执行,依赖多核CPU或多处理器。
- 并发:任务交替执行(单核)或同时执行(多核),通过调度模拟“同时性”。
- 同步异步、阻塞非阻塞
- 同步:任务按顺序执行,前一个任务未完成时,后续任务必须等待。
- 异步:任务发起后,不等待其完成,继续执行后续操作,通过回调/通知获取结果。
- 阻塞:线程在等待某个操作(如I/O、锁)完成时,
暂停执行,交出CPU控制权。 - 非阻塞:线程在等待操作完成时
继续执行其他任务,通过轮询或回调检查结果。 - 同步阻塞:单线程调用阻塞式I/O(如传统文件读取)
- 同步非阻塞:循环调用非阻塞I/O,不断检查数据是否就绪。
- 异步阻塞:错误设计(如在异步操作中使用阻塞调用)。
- 异步非阻塞:异步I/O(如Node.js的fs.readFile),发起请求后继续执行其他任务,通过回调或Promise处理结果。
线程的创建
-
继承Thread类 重写run方法
public class MiTest {public static void main(String[] args) {MyJob t1 = new MyJob();t1.start();for (int i = 0; i < 100; i++) {System.out.println("main:" + i);}} } class MyJob extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("MyJob:" + i);}} } -
实现Runnable接口 重写run方法
public class MiTest {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread t1 = new Thread(myRunnable);t1.start();for (int i = 0; i < 1000; i++) {System.out.println("main:" + i);}} } class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println("MyRunnable:" + i);}} }- 匿名内部类方式
Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println("匿名内部类:" + i);}} }); - lambda方式
Thread t2 = new Thread(() -> {for (int i = 0; i < 100; i++) {System.out.println("lambda:" + i);} });
- 匿名内部类方式
-
实现Callable 重写call方法,配合FutureTask
Callable一般用于有返回结果的非阻塞的执行方法,同步非阻塞。public class MiTest {public static void main(String[] args) throws ExecutionException, InterruptedException {//1. 创建MyCallableMyCallable myCallable = new MyCallable();//2. 创建FutureTask,传入CallableFutureTask futureTask = new FutureTask(myCallable);//3. 创建Thread线程Thread t1 = new Thread(futureTask);//4. 启动线程t1.start();//5. 做一些操作//6. 要结果Object count = futureTask.get();System.out.println("总和为:" + count);} } class MyCallable implements Callable{@Overridepublic Object call() throws Exception {int count = 0;for (int i = 0; i < 100; i++) {count += i;}return count;} } -
基于线程池构建线程
// 创建线程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); while(true) {threadPool.execute(new Runnable() { // 提交多个线程任务,并执行@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " is running ..");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}}); }
线程的状态

- 新建状态(NEW):当程序使用
new 关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM 为其分配内存,并初始化其成员变量的值。 - 就绪状态(RUNNABLE):当线程
对象调用了 start()方法之后,该线程处于就绪状态。 Java 虚拟机会为其创建方法调用栈和程序计数器,等待调度运行。 - 运行状态(RUNNING):
就绪状态的线程获得了 CPU,开始执行 run()方法的线程执行体,则该线程处于运行状态。 - 阻塞状态(BLOCKED):线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice,暂时停止运行。直到线程进入可运(runnable)状态,才有机会再次获得 cpu timeslice 转到运行(running)状态。
阻塞的情况分三种:- 等待阻塞(o.wait->等待对列):运行(running)的线程执行 o.wait()方法, JVM 会把该线程放入等待队列(waitting queue)中。
- 同步阻塞(lock->锁池):运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池(lock pool)中。
- 其他阻塞(sleep/join):运行(running)的线程执行Thread.sleep(long ms)或 t.join()方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。当 sleep()状态超时、 join()等待线程终止或者超时、或者 I/O处理完毕时,线程重新转入可运行(runnable)状态。
- 等待状态(WATING):
调用wait方法就会处于WAITING状态,需要被手动唤醒。 - 时间等待状态(TIMED_WATING):
调用sleep方法或者join方法,会被自动唤醒,无需手动唤醒。 - 结束状态(TERMINATED):线程会以下面三种方式结束,结束后就是死亡状态。
- run()或 call()方法执行完成,线程正常结束。异常结束。
- 线程抛出一个未捕获的 Exception 或 Error。调用 stop。
- 直接调用该线程的 stop()方法来结束该线程—该方法通常容易导致死锁,不推荐使用。
线程的终止方式
- 正常运行结束:程序运行结束,线程自动结束。
- 使用volatile修饰的共享变量(很少会用):通过修改共享变量在破坏死循环,让线程退出循环,结束run方法。
- Interrupt 方法结束线程:
- 线程处于阻塞状态:如使用了 sleep,同步锁的 wait,socket 中的 receiver,accept 等方法时,会使线程处于阻塞状态。当调用线程的interrupt()方法时,会抛出 InterruptException 异常。阻塞中的那个方法抛出这个异常,通过代码捕获该异常,然后 break 跳出循环状态,从而让我们有机会结束这个线程的执行。 通常很多人认为只要调用 interrupt 方法线程就会结束,实际上是错的, 一定要
先捕获InterruptedException 异常之后通过 break 来跳出循环,才能正常结束 run 方法。 - 线程未处于阻塞状态:使用 isInterrupted()判断线程的中断标志来退出循环。当使用interrupt()方法时,中断标志就会置 true,和使用自定义的标志来控制循环是一样的道理。
- 线程处于阻塞状态:如使用了 sleep,同步锁的 wait,socket 中的 receiver,accept 等方法时,会使线程处于阻塞状态。当调用线程的interrupt()方法时,会抛出 InterruptException 异常。阻塞中的那个方法抛出这个异常,通过代码捕获该异常,然后 break 跳出循环状态,从而让我们有机会结束这个线程的执行。 通常很多人认为只要调用 interrupt 方法线程就会结束,实际上是错的, 一定要
- stop 方法终止线程(线程不安全):强制让线程结束。
start 与 run 区别
- start() 方法来
启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕,可以直接继续执行下面的代码。 - 通过调用 Thread 类的 start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。
- 方法 run()称为
线程体,它包含了要执行的这个线程的内容,线程就进入了运行状态,开始运行 run 函数当中的代码。 Run 方法运行
结束, 此线程终止。然后 CPU 再调度其它线程。
线程的常用方法
-
notify()和notifyAll()有什么区别- 唤醒的线程数量
- notify():随机唤醒
一个正在该对象上调用wait()的线程(无法指定具体唤醒哪个线程)。 - notifyAll():唤醒
所有正在该对象上调用wait()的线程。
- notify():随机唤醒
- 适用场景
- notify():当所有等待线程的
逻辑等价(即任何一个线程被唤醒都能完成后续任务)时,使用 notify() 更高效。 - notifyAll():当等待线程的
条件不同(需要确保所有等待线程都有机会重新检查条件)时,使用 notifyAll() 更安全。
- notify():当所有等待线程的
- 潜在风险
- notify() :如果多个线程等待的条件不同,可能唤醒错误的线程(例如生产者唤醒了另一个生产者),导致某些线程永远无法被唤醒(
线程饥饿或死锁)。 - notifyAll():唤醒所有线程会增加锁竞争,可能降低性能(但在现代 JVM 中影响较小)。
- notify() :如果多个线程等待的条件不同,可能唤醒错误的线程(例如生产者唤醒了另一个生产者),导致某些线程永远无法被唤醒(
- 唤醒的线程数量
-
sleep()和wait()有什么区别- 所属类与调用对象
- sleep()
- 属于
Thread类的静态方法。 - 直接通过
Thread.sleep(ms)调用,作用于当前线程。
- 属于
- wait()
- 属于
Object类的实例方法。 - 必须在同步块(
synchronized)中调用,作用于某个对象(如obj.wait())。
- 属于
- sleep()
- 锁的行为
- sleep()
- 不释放锁:线程休眠期间,仍然持有对象的锁(如果已获得)。
- 其他线程无法进入该对象的同步块。
- wait()
- 释放锁:调用后线程会释放对象的锁,允许其他线程获取锁并执行同步代码。
- 线程进入等待队列,直到被 notify()/notifyAll() 唤醒或超时。
- sleep()
- 使用场景
- sleep()
- 单纯让线程暂停执行一段时间,不涉及线程间协作。
- 示例:模拟耗时操作,定时任务间隔。
- wait()
- 用于线程间通信,需结合
notify()/notifyAll()实现条件等待。 - 示例:生产者-消费者模型中,消费者等待队列有数据。
- 用于线程间通信,需结合
- sleep()
- 唤醒机制
- sleep():休眠结束后自动恢复,无需外部唤醒(除非被
interrupt()中断)。 - wait():必须通过其他线程调用
notify()/notifyAll()唤醒,或等待超时(若指定了超时时间)。
- sleep():休眠结束后自动恢复,无需外部唤醒(除非被
- 所属类与调用对象
-
异常处理
- 共同点:两者都可能抛出
InterruptedException(当线程在等待/休眠期间被中断时)。 - 区别
wait()必须在同步块中调用,否则抛IllegalMonitorStateException。sleep()无此限制。
- 共同点:两者都可能抛出
-
线程状态
- sleep():线程进入
TIMED_WAITING状态(若指定时间)或WAITING(若时间无限,但实际sleep()必须指定时间)。 - wait():线程进入
WAITING状态(无超时)或TIMED_WAITING状态(有超时)。
- sleep():线程进入
-
Java中
interrupted和isInterrupted方法的区别-
方法定义与作用对象
方法 类型 作用对象 描述 Thread.interrupted() 静态方法 当前执行线程 检查并清除当前线程的中断状态。 thread.isInterrupted() 实例方法 调用该方法的线程对象 仅检查线程的中断状态,不修改状态。
-
-
Thread类中的
yield方法有什么作用- Thread.yield() 是一个用于线程调度的静态方法,其主要作用是 提示当前线程让出CPU资源,允许其他线程(尤其是优先级相同或更高的线程)有机会执行。
锁
锁的分类
- 悲观锁
- 乐观锁
- 可重入锁
- 不可重入锁
- 公平锁
- 非公平锁
- 互斥锁
- 共享锁
深入synchronized
深入ReentrantLock
死锁问题
- 死锁产生的必要条件
- 互斥(Mutual Exclusion):资源一次只能被一个线程占用。
- 请求与保持(Hold and Wait):线程持有至少一个资源,同时请求其他线程持有的资源。
- 不可剥夺(No Preemption):资源只能由持有它的线程主动释放,不能被强制剥夺。
- 循环等待(Circular Wait):多个线程形成环形等待链,每个线程都在等待下一个线程释放资源。
- 常见死锁场景
- 场景1:嵌套锁顺序不一致
// 线程1 synchronized (lockA) {synchronized (lockB) { ... } }// 线程2 synchronized (lockB) {synchronized (lockA) { ... } // 可能导致死锁 } - 场景2:生产者-消费者模型中的资源竞争
多个线程在共享队列中同时获取插入和删除的锁。 - 场景3:数据库事务中的行级锁
多个事务以不同顺序更新相同的数据行。
- 场景1:嵌套锁顺序不一致
- 如何检测死锁
- JVM工具
- 使用
jstack <pid>导出线程栈,查找Found one Java-level deadlock。 - 使用
jconsole或VisualVM查看线程状态。
- 使用
- 代码检测
ThreadMXBean bean = ManagementFactory.getThreadMXBean(); long[] threadIds = bean.findDeadlockedThreads(); if (threadIds != null) {System.out.println("检测到死锁!"); }
- JVM工具
- 死锁预防与解决
- 方法1:统一加锁顺序
- 核心思想:确保所有线程按相同的顺序请求资源。
- 示例:在转账场景中,按账户哈希值排序锁。
void transfer(Account from, Account to, int amount) {Account first = from.hashCode() < to.hashCode() ? from : to;Account second = from.hashCode() < to.hashCode() ? to : from;synchronized (first) {synchronized (second) {// 转账逻辑}} }
- 方法2:避免嵌套锁
- 尽量减少锁的作用域,使用无锁设计(如CAS操作)或线程安全容器(如
ConcurrentHashMap)。
- 尽量减少锁的作用域,使用无锁设计(如CAS操作)或线程安全容器(如
- 方法3:设置超时等待
- 使用
tryLock替代synchronized,设定超时时间。Lock lockA = new ReentrantLock(); Lock lockB = new ReentrantLock(); if (lockA.tryLock(1, TimeUnit.SECONDS)) {try {if (lockB.tryLock(1, TimeUnit.SECONDS)) {try { ... } finally { lockB.unlock(); }}} finally { lockA.unlock(); } }
- 使用
- 方法4:破坏不可剥夺条件
- 允许系统强制回收资源(需谨慎,可能导致数据不一致)。
- 方法5:使用线程池隔离
- 将不同任务分配到独立的线程池,避免资源竞争。
- 方法1:统一加锁顺序
阻塞队列
- 阻塞队列原理
- 当
队列中没有数据的情况下,消费者端的所有线程都会被自动阻塞(挂起),直到有数据放入队列。 - 当
队列中填满数据的情况下,生产者端的所有线程都会被自动阻塞(挂起),直到队列中有空的位置,线程被自动唤醒。
- 当
- 阻塞队列的主要方法

- 阻塞队列分类
- ArrayBlockingQueue :由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue :由链表结构组成的有界阻塞队列。
- PriorityBlockingQueue :支持优先级排序的无界阻塞队列。
- DelayQueue:使用优先级队列实现的无界阻塞队列。
- SynchronousQueue:不存储元素的阻塞队列。
- LinkedTransferQueue:由链表结构组成的无界阻塞队列。
- LinkedBlockingDeque:由链表结构组成的双向阻塞队列
线程池
-
线程池分类
- newCachedThreadPool:
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。 - newFixedThreadPool:
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线。 - newScheduledThreadPool:
创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 - newSingleThreadExecutor:Executors.newSingleThreadExecutor()
返回一个线程池(这个线程池只有一个线程) ,这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去。
- newCachedThreadPool:
-
线程池配置属性
- 核心线程数(corePoolSize)
- 作用:线程池中始终保持的最小线程数,即使这些线程处于空闲状态。
- 注意事项:
- 默认情况下,核心线程不会因空闲而被回收(除非配置了
allowCoreThreadTimeOut)。 - 适用于处理常规任务负载。
- 默认情况下,核心线程不会因空闲而被回收(除非配置了
- 最大线程数(maximumPoolSize)
- 作用:线程池允许创建的最大线程数量。当工作队列已满且当前线程数小于最大线程数时,会创建新线程处理任务。
- 注意事项:
- 需根据系统资源(如CPU核数、内存)和任务类型(CPU密集型或IO密集型)合理设置。
- 若设置为与corePoolSize相同,则线程池退化为固定大小线程池。
- 空闲线程存活时间(keepAliveTime)
- 作用:非核心线程(超出corePoolSize的线程)在空闲状态下的存活时间。超过该时间后,线程会被终止回收。
- 单位:通常与TimeUnit配合使用(如秒、毫秒)。
- 注意事项:
- 若允许核心线程超时(通过
allowCoreThreadTimeOut(true)),核心线程也会受此参数影响。
- 若允许核心线程超时(通过
- 工作队列(workQueue)
- 作用:用于保存等待执行任务的阻塞队列。
常见类型:- 无界队列(如
LinkedBlockingQueue):可能导致内存溢出。 - 有界队列(如
ArrayBlockingQueue):需合理设置容量。 - 同步移交队列(如
SynchronousQueue):不存储任务,直接将任务交给线程。
- 无界队列(如
- 选择策略:
- 任务量波动大时,优先使用有界队列避免资源耗尽。
- 高吞吐场景可使用
LinkedBlockingQueue,快速响应场景可使用SynchronousQueue。
- 作用:用于保存等待执行任务的阻塞队列。
- 线程工厂(threadFactory)
- 作用:自定义线程创建方式,可设置线程名称、优先级、是否为守护线程等。
- 示例:
ThreadFactory factory = r -> {Thread t = new Thread(r);t.setName("my-pool-" + t.getId());return t; };
- 拒绝策略(RejectedExecutionHandler)
- 作用:当线程池无法接受新任务(队列已满且线程数达上限)时的处理策略。
常见策略:- AbortPolicy(默认):抛出
RejectedExecutionException。 - CallerRunsPolicy:由提交任务的线程直接执行任务。
- DiscardPolicy:静默丢弃被拒绝的任务。
- DiscardOldestPolicy:丢弃队列中最旧的任务,并重新提交被拒绝的任务。
- AbortPolicy(默认):抛出
- 自定义策略:可扩展RejectedExecutionHandler接口实现特定逻辑(如记录日志或重试)。
- 作用:当线程池无法接受新任务(队列已满且线程数达上限)时的处理策略。
- 其他配置
- allowCoreThreadTimeOut:允许核心线程因空闲超时被回收(需
keepAliveTime > 0)。 - 预热核心线程:通过
prestartAllCoreThreads()提前启动所有核心线程。
- allowCoreThreadTimeOut:允许核心线程因空闲超时被回收(需
- 核心线程数(corePoolSize)
相关文章:
多线程与并发编程 面试专题
多线程与并发编程 面试专题 线程的基础概念基础概念线程的创建线程的状态线程的终止方式start 与 run 区别线程的常用方法 锁锁的分类深入synchronized深入ReentrantLock死锁问题 阻塞队列线程池 线程的基础概念 基础概念 进程与线程 进程:指运行中的程序。 比如我…...
米尔电子-LR3568-烧录鸿蒙
最近开始搞鸿蒙,用的是米尔的LR3568。 开贴记录。 首先要在LR3568上烧录鸿蒙 一、安装准备 1.从米尔电子上下载资料 网址:米尔开发者中心 注册完成后,进入页面,选择我的产品,添加PN和SN PN和SN可以在包装盒上找到 添加到这里…...
Redis Sentinel 及 Redisson 连接问题全解析
在 Kubernetes (k8s) 环境下使用 Redis Sentinel 进行高可用部署时,可能会遇到 failover 超时、Sentinel 误判、Spring Boot 连接失败 以及 Redisson 配置错误等问题。本文将对这些问题进行汇总分析,并提供详细的解决方案。 1️⃣ Redis Sentinel 介绍 …...
基于Flink SQL的实时指标多维分析模型
数据流程介绍 1.创建源表kafka接入消息队列数据,定义字段映射规则; 2.创建目标表es_sink配置Elasticsearch输出; 3.通过多级视图(tmp→tmp_dedup→tmp1/tmp2→tmp3→tmp_groupby)实现数据清洗、去重、状态计算&#x…...
算法刷题整理合集(一)
算法刷题整理合集(一) 本篇博客旨在记录自已的算法刷题练习成长,里面注有详细的代码注释以及和个人的思路想法,希望可以给同道之人些许帮助。本人也是算法小白,水平有限,如果文章中有什么错误或遗漏之处&am…...
C++ STL—— String库
在C编程中,字符串操作是几乎每个项目都会涉及的基础功能。C标准模板库(STL)中的string类为我们提供了强大而灵活的工具,使得字符串的处理变得简单高效。无论是字符串的创建、修改、查找,还是复杂的文本处理,…...
【从零开始学习计算机科学】数据库系统(二)关系数据库 与 关系代数
【从零开始学习计算机科学】数据库系统(二)关系数据库 与 关系代数 关系数据库结构化查询语言SQL数据定义语言(DDL)数据查询语言(Data Query Language, DQL)数据操纵语言(Data Manipulation Language, DML)数据控制语言(Data Control Language, DCL)关系型数据库的优…...
DoS攻击防范
一、网络架构优化 使用CDN或反向代理 通过内容分发网络(CDN)或反向代理(如Nginx)分散流量,将请求分发到多个服务器节点,减轻单点压力,同时过滤异常请求。 负载均衡技术 部署负载均衡设备&#…...
Linux驱动开发实战(四):设备树点RGB灯
Linux驱动开发实战(四):设备树点RGB灯 文章目录 Linux驱动开发实战(四):设备树点RGB灯前言一、驱动实现1.1 驱动设计思路1.2 关键数据结构1.3 字符设备操作函数1.4 平台驱动探测函数1.5 匹配表和平台驱动结…...
vue中,watch里,this为undefined的两种解决办法
提示:vue中,watch里,this为undefined的两种解决办法 文章目录 [TOC](文章目录) 前言一、问题二、方法1——使用function函数代替箭头函数()>{}三、方法2——使用that总结 前言 尽量使用方法1——使用function函数代替箭头函数()…...
设计模式C++
针对一些经典的常见的场景, 给定了一些对应的解决方案,这个就叫设计模式。 设计模式的作用:使代码的可重用性高,可读性强,灵活性好,可维护性强。 设计原则: 单一职责原则:一个类只做一方面的…...
前端构建工具进化论:从Grunt到Turbopack的十年征程
前端构建工具进化论:从Grunt到Turbopack的十年征程 一、石器时代:任务自动化工具(2012-2014) 1.1 Grunt:首个主流构建工具 // Gruntfile.js 典型配置 module.exports function(grunt) {grunt.initConfig({concat: {…...
设备预测性维护:企业降本增效的关键密码
在当今竞争激烈的商业战场中,企业犹如一艘在波涛汹涌大海上航行的巨轮,要想乘风破浪、稳步前行,降本增效便是那至关重要的 “船锚”,帮助企业在复杂的市场环境中站稳脚跟。而设备预测性维护,正是开启企业降本增效大门的…...
css基本功
为什么 ::first-letter 是伪元素? ::first-letter 的作用是选择并样式化元素的第一个字母,它创建了一个虚拟的元素来包裹这个字母,因此属于伪元素。 grid布局 案例一 <!DOCTYPE html> <html lang"zh-CN"><head&…...
信号处理抽取多项滤波的数学推导与仿真
昨天的《信号处理之插值、抽取与多项滤波》,已经介绍了插值抽取的多项滤率,今天详细介绍多项滤波的数学推导,并附上实战仿真代码。 一、数学变换推导 1. 多相分解的核心思想 将FIR滤波器的系数 h ( n ) h(n) h(n)按相位分组,每…...
C++双端队列知识点+习题
在C中,双端队列(Deque,发音为“deck”)是标准模板库(STL)中的一种容器适配器,其全称为Double-Ended Queue。它结合了队列和栈的特点,允许在容器的两端(前端和后端&#x…...
【递归、搜索和回溯算法】专题二 :二叉树中的深搜
二叉树中的深搜 深度优先遍历(DFS):一种沿着树或图的深度遍历节点的算法,尽可能深地搜索树或图的分支,如果一条路径上的所有结点都被遍历完毕,就会回溯到上一层,继续找一条路遍历。 在二叉树中…...
Vue3计算属性深度解析:经典场景与Vue2对比
一、计算属性的核心价值 计算属性(Computed Properties)是Vue响应式系统的核心特性之一,它通过依赖追踪和缓存机制优雅地解决模板中复杂逻辑的问题。当我们需要基于现有响应式数据进行派生计算时,计算属性总能保持高效的性能表现…...
UE5与U3D引擎对比分析
Unreal Engine 5(UE5)和Unity 3D(U3D)是两款主流的游戏引擎,适用于不同类型的项目开发。以下是它们的主要区别,分点整理: 1. 核心定位 UE5: 主打3A级高画质项目(如主机/P…...
【vue3学习笔记】(第150-151节)computed计算属性;watch监视ref定义的数据
尚硅谷Vue2.0Vue3.0全套教程丨vuejs从入门到精通 本篇内容对应课程第150-151节 课程 P150节 《computed计算属性》笔记 写一个简单的 姓、名输入框效果: 用vue2的形式定义一个计算属性 fullName: 测试页面展示无问题: 但是,在vue…...
JavaScript如何实现复制图片功能?
最近开发中遇到一个需求,就是用户希望能通过直接点击按钮复制图片,然后就可以很方便的把图片发送到班群中,于是就有了复制图片的需求。 那么如何通过JavaScript来实现复制图片呢? 一、前置知识:如何实现复制…...
MySQL 8 设置允许远程连接(Windows环境)
🌟 MySQL 8 设置允许远程连接(Windows环境) 在开发和部署应用时,经常需要从远程主机连接到MySQL数据库。默认情况下,MySQL仅允许本地连接,因此需要进行一些配置才能允许远程访问。今天,我将详细…...
我又又又又又又更新了~~纯手工编写C++画图,有注释~~~
再再再次感谢Ttcofee提的问题 本次更新内容: 鼠标图案(切换),版本号获取,输入框复制剪切板 提前申明:如果运行不了,请到主页查看RedpandaDevc下载,若还是不行就卸了重装。 版本号&…...
Python控制语句——循环语句-for
1.下面的语句哪个会无限循环下去()。 A、 for a in range(10): time.sleep(10) B、 while 1<10: time.sleep(10) C、 while True: break D、 a = [3,-1,2] for i in a: if i==-1: break 答案:B。1<10始终为True,循环体中又没有break的条件,故B会无限循环。 2.for s i…...
全面解析:将采购入库单数据集成到MySQL的技术实施
旺店通旗舰版-采购入库单集成到MySQL的技术案例分享 在数据驱动的业务环境中,如何高效、准确地实现系统间的数据对接是企业面临的重要挑战。本文将聚焦于一个具体的系统对接集成案例:将旺店通旗舰奇门平台上的采购入库单数据集成到MySQL数据库中&#x…...
12. Pandas :使用pandas读Excel文件的常用方法
一 read_excel 函数 其他参数根据实际需要进行查找。 1.接受一个工作表 在 11 案例用到的 Excel 工作簿中,数据是从第一张工作表的 A1 单元格开始的。但在实际场景中, Excel 文件可能并没有这么规整。所以 panda 提供了一些参数来优化读取过程。 比如 s…...
记录致远OA服务器硬盘升级过程
前言 日常使用中OA系统突然卡死,刷新访问进不去系统,ping服务器地址正常,立马登录服务器检查,一看磁盘爆了。 我大脑直接萎缩了,谁家OA系统配400G的空间啊,过我手的服务器没有50也是30台,还是…...
Java网络多线程
网络相关概念: 关于访问: IP端口 因为一个主机上可能有多个服务, 一个服务监听一个端口,当你访问的时候主机通过端口号就能知道要和哪个端口发生通讯.因此一个主机上不能有两个及以上的服务监听同一个端口. 协议简单来说就是数据的组织形式 好像是两个人交流一样,要保证自己说…...
【H2O2 | 软件开发】Axios发送Http请求
目录 前言 开篇语 准备工作 正文 概念 封装工具包 示例 结束语 前言 开篇语 本系列为短篇,每次讲述少量知识点,无需一次性灌输太多的新知识点。该主题文章主要是围绕前端、全栈开发相关面试常见问题撰写的,希望对诸位有所帮助。 如…...
VScode 运行LVGL
下载vscode解压 环境安装 安装mingw64,gcc 版本必须8.3以上 安装cmak 系统环境变量Path中添加(以实际安装目录为准) C:\Program Files\mingw64\bin C:\Program Files\CMake\bin 将GUI-Guider生成的代码目录拷贝一份放到vscode项目目录…...
