Java面试黄金宝典6

1. 什么是 CAS

  • 原理
    1. CAS (Compare-And-Swap)是一种硬件级别的原子操作指令,在 Java 并发编程中常被用于实现无锁算法。其核心逻辑是:在进行数据更新时,会先将内存位置 V 的值与预期原值 A 进行比较,如果二者相等,就把内存位置 V 的值更新为新值 B;若不相等,则说明在这期间有其他线程对该内存位置的值进行了修改,此时不会执行更新操作。
    2. 在 Java 里,sun.misc.Unsafe 类提供了对 CAS 操作的支持。不过,Unsafe 类的使用具有一定的风险性,所以 Java 又在 java.util.concurrent.atomic 包下提供了一系列基于 CAS 实现的原子类,像 AtomicIntegerAtomicLong 等。
  • 要点
    1. 原子性:CAS 操作由硬件指令直接保障其原子性,在执行过程中不会被其他线程中断,能有效避免多线程环境下的数据竞争问题。
    2. 无锁机制:与传统的加锁机制不同,CAS 操作无需加锁,这样就避免了线程阻塞和上下文切换带来的开销,从而提升了并发性能。
    3. ABA 问题:这是 CAS 操作面临的主要问题。当一个值从 A 变为 B 后又变回 A 时,CAS 操作会认为值没有发生改变,进而继续执行更新操作,然而实际上该值已经经历了变化。
  • 应用
    1. 解决 ABA 问题:可以借助带有时间戳的原子引用 AtomicStampedReference 来解决 ABA 问题。AtomicStampedReference 不仅会比较值是否相等,还会比较时间戳是否一致,只有当值和时间戳都匹配时才会执行更新操作。
    2. 应用场景:CAS 广泛应用于并发容器、原子类以及一些无锁算法的实现中,例如 Java 中的 ConcurrentHashMap 在部分操作上就运用了 CAS 来保证并发安全。

2. 什么是可重入锁 ReentrantLock

  • 原理
    1. ReentrantLockjava.util.concurrent.locks 包下的一个类,它实现了 Lock 接口,是一种可重入的互斥锁。可重入意味着同一个线程能够多次获取同一把锁而不会被阻塞。每获取一次锁,锁的内部计数器就会加 1;每释放一次锁,计数器就会减 1。当计数器的值变为 0 时,锁才会被完全释放。
    2. ReentrantLock 内部通过 AbstractQueuedSynchronizer(AQS)来实现锁的状态管理和线程的排队等待。
  • 要点
    1. 手动锁管理:与 synchronized 不同,ReentrantLock 需要手动调用 lock() 方法来获取锁,调用 unlock() 方法来释放锁,通常要在 finally 块中调用 unlock() 以确保锁一定会被释放。
    2. 灵活性ReentrantLock 提供了更多的灵活性,比如可以选择实现公平锁或非公平锁,还支持中断锁等待、限时锁等待等功能。
    3. 条件变量ReentrantLock 可以配合 Condition 对象使用,实现更精细的线程间通信和同步。
  • 应用
    1. 公平锁和非公平锁的选择:公平锁能够保证线程获取锁的公平性,但会增加线程上下文切换的开销;非公平锁虽然可能导致某些线程长时间得不到锁,但可以提高系统的吞吐量。在实际应用中,需要根据具体的业务场景来选择合适的锁类型。
    2. synchronized 的性能比较:在竞争不激烈的情况下,synchronizedReentrantLock 的性能相差不大;但在竞争激烈的场景下,ReentrantLock 的性能可能会更优。

3. 什么是乐观锁和悲观锁,阻塞锁,自旋锁,偏向锁,轻量锁,重量锁,公平锁,非公平锁,有哪些区别

  • 乐观锁和悲观锁
    • 原理
      1. 乐观锁:它假定数据在大多数情况下不会发生冲突,所以在操作数据时不会加锁,而是在更新数据时检查数据是否被其他线程修改过。通常是通过版本号或时间戳等机制来实现。
      2. 悲观锁:它假定数据很容易发生冲突,因此在操作数据之前会先加锁,防止其他线程访问,确保在自己操作数据期间数据不会被其他线程修改。
    • 区别
      1. 适用场景:乐观锁适用于读多写少的场景,因为在这种场景下数据发生冲突的概率较低,使用乐观锁可以减少加锁和解锁的开销;悲观锁适用于写多读少的场景,因为在这种场景下数据发生冲突的概率较高,使用悲观锁可以保证数据的一致性。
      2. 性能:乐观锁的开销较小,因为它不需要加锁和解锁的操作;悲观锁的开销较大,因为它需要进行加锁和解锁操作,并且可能会导致线程阻塞和上下文切换。
  • 阻塞锁和自旋锁
    • 原理
      1. 阻塞锁:当一个线程获取不到锁时,会被阻塞进入等待状态,直到锁被释放。此时线程会让出 CPU 资源,进入睡眠状态,等待其他线程释放锁后再被唤醒。
      2. 自旋锁:当一个线程获取不到锁时,不会进入阻塞状态,而是不断地循环尝试获取锁,在这个过程中线程会一直占用 CPU 资源。
    • 区别
      1. CPU 资源占用:阻塞锁会让出 CPU 资源,不会一直占用 CPU;自旋锁会一直占用 CPU 资源,直到获取到锁或达到自旋的最大次数。
      2. 适用场景:阻塞锁适用于锁的持有时间较长的场景,因为在这种情况下线程阻塞等待可以避免 CPU 资源的浪费;自旋锁适用于锁的持有时间较短的场景,因为在这种情况下线程不断自旋尝试获取锁的开销相对较小。
  • 偏向锁、轻量锁和重量锁
    • 原理
      1. 偏向锁:在只有一个线程访问同步块时,会将锁偏向该线程。当该线程再次进入同步块时,无需进行任何同步操作,直接进入同步块执行代码,这样可以减少加锁和解锁的开销。
      2. 轻量锁:当有多个线程交替访问同步块时,会使用轻量锁。轻量锁通过 CAS 操作来获取和释放锁,避免了使用操作系统的互斥量,从而减少了线程阻塞和上下文切换的开销。
      3. 重量锁:当多个线程同时竞争锁时,轻量锁会升级为重量锁。重量锁使用操作系统的互斥量来实现同步,会导致线程阻塞和上下文切换,开销较大。
    • 区别
      1. 适用场景:偏向锁适用于单线程场景,开销最小;轻量锁适用于多线程交替访问场景,开销较小;重量锁适用于多线程同时竞争场景,开销最大。
      2. 锁的升级:锁的状态会根据线程的竞争情况进行升级,即从偏向锁升级为轻量锁,再升级为重量锁,但锁的状态不会降级。
  • 公平锁和非公平锁
    • 原理
      1. 公平锁:多个线程按照申请锁的顺序来获取锁,先到先得。当一个线程释放锁后,会从等待队列中选择最早申请锁的线程来获取锁。
      2. 非公平锁:多个线程获取锁的顺序是不确定的,可能后申请的线程先获取到锁。当一个线程释放锁后,任何一个等待的线程都有机会获取锁。
    • 区别
      1. 公平性:公平锁可以保证线程获取锁的公平性,避免某些线程长时间得不到锁;非公平锁可能会导致某些线程长时间得不到锁。
      2. 性能:公平锁会增加线程上下文切换的开销,因为每次释放锁后都需要从等待队列中选择下一个线程;非公平锁可以提高系统的吞吐量,因为它减少了线程上下文切换的次数。

4. 什么是 ReentrantLock 和 synchronized,有什么区别

  • 原理
    1. synchronized:它是 Java 中的关键字,是一种内置的锁机制。当 synchronized 修饰实例方法时,使用的是对象锁;当修饰静态方法时,使用的是类锁;当修饰代码块时,需要指定锁对象。synchronized 会在进入同步块或同步方法时自动获取锁,在退出时自动释放锁。
    2. ReentrantLock:它是 Java 中的一个类,实现了 Lock 接口,是一种可重入的互斥锁。通过调用 lock() 方法来获取锁,调用 unlock() 方法来释放锁。
  • 区别
    • 锁的获取和释放方式
      1. synchronized 是自动获取和释放锁,由 Java 虚拟机负责管理锁的生命周期,使用起来比较简单。
      2. ReentrantLock 需要手动获取和释放锁,使用时需要在 finally 块中调用 unlock() 方法,以确保锁一定会被释放,否则可能会导致死锁。
    • 锁的灵活性
      1. synchronized 是一种比较简单的锁机制,缺乏灵活性,只能实现非公平锁,并且不支持中断锁等待和限时锁等待等功能。
      2. ReentrantLock 提供了更多的灵活性,可以选择实现公平锁或非公平锁,支持中断锁等待、限时锁等待等功能,还可以配合 Condition 对象实现更精细的线程间通信和同步。
    • 性能
      1. 在竞争不激烈的情况下,synchronizedReentrantLock 的性能相差不大,因为 Java 虚拟机对 synchronized 进行了很多优化,如偏向锁、轻量锁等。
      2. 在竞争激烈的情况下,ReentrantLock 的性能可能会更好,因为它可以根据具体的业务场景选择合适的锁类型,并且可以避免一些不必要的线程阻塞和上下文切换。

5. 重入锁、对象锁、类锁的关系

  • 重入锁
    • 重入锁指的是同一个线程能够多次获取同一把锁而不会被阻塞。ReentrantLocksynchronized 都是可重入锁。当一个线程第一次获取锁时,锁的计数器会加 1,之后该线程再次获取同一把锁时,计数器会继续加 1;每次释放锁时,计数器会减 1,当计数器为 0 时,锁才会被完全释放。
  • 对象锁
    • synchronized 修饰实例方法或代码块时使用的是对象锁。同一时刻只有一个线程可以访问该对象的被 synchronized 修饰的方法或代码块。不同的对象实例拥有各自独立的对象锁,一个线程可以同时获取多个不同对象的对象锁。
  • 类锁
    • synchronized 修饰静态方法或代码块时使用的是类锁。类锁是基于类的 Class 对象实现的,同一时刻只有一个线程可以访问该类的被 synchronized 修饰的静态方法或代码块。所有该类的对象实例共享同一个类锁。
  • 关系
    • 对象锁和类锁都是基于重入锁的机制实现的,它们的区别在于锁的范围不同。对象锁是针对对象实例的,不同的对象实例可以同时执行被 synchronized 修饰的实例方法或代码块;类锁是针对类的,所有该类的对象实例都需要竞争同一个类锁。

6. 如何创建线程?哪种好?

  • 创建线程的方式
    • 继承 Thread
      • 创建一个类继承 Thread 类,并重写 run() 方法,run() 方法中包含了线程要执行的代码。然后创建该类的实例并调用 start() 方法启动线程。

java

class MyThread extends Thread {@Overridepublic void run() {System.out.println("Thread is running");}
}public class Main {public static void main(String[] args) {MyThread thread = new MyThread();thread.start();}
}

  • 实现 Runnable 接口
    • 创建一个类实现 Runnable 接口,实现 run() 方法。然后创建该类的实例并将其作为参数传递给 Thread 类的构造函数,最后调用 start() 方法启动线程。

java

class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("Thread is running");}
}public class Main {public static void main(String[] args) {MyRunnable runnable = new MyRunnable();Thread thread = new Thread(runnable);thread.start();}
}

  • 实现 Callable 接口
    • 创建一个类实现 Callable 接口,实现 call() 方法,call() 方法可以有返回值。然后使用 FutureTask 类将 Callable 对象包装起来,再将 FutureTask 对象作为参数传递给 Thread 类的构造函数,最后调用 start() 方法启动线程。可以通过 FutureTaskget() 方法获取 call() 方法的返回值。

java

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {return 1 + 2;}
}public class Main {public static void main(String[] args) throws Exception {MyCallable callable = new MyCallable();FutureTask<Integer> futureTask = new FutureTask<>(callable);Thread thread = new Thread(futureTask);thread.start();System.out.println(futureTask.get());}
}

  • 哪种好
    • 实现 Runnable 接口和 Callable 接口的方式更好。因为 Java 是单继承的,继承 Thread 类会限制类的扩展性,一个类一旦继承了 Thread 类,就不能再继承其他类了。而实现 Runnable 接口和 Callable 接口可以让类继承其他类,同时还能实现多线程功能。此外,实现 Runnable 接口和 Callable 接口的方式可以更好地实现资源共享,多个线程可以共享同一个 RunnableCallable 对象。

7. 线程具体有哪些状态

Java 中的线程有以下六种状态,定义在 Thread.State 枚举中:

  • NEW:线程刚被创建,但还没有调用 start() 方法。此时线程还没有进入运行状态,只是在 Java 虚拟机中分配了一些内存空间。
  • RUNNABLE:线程正在 Java 虚拟机中执行,或者准备执行。它包含了操作系统线程状态中的就绪和运行两种状态。当线程调用 start() 方法后,会进入 RUNNABLE 状态,此时线程可能正在 CPU 上执行,也可能在等待 CPU 资源。
  • BLOCKED:线程正在等待获取一个监视器锁,进入同步块或同步方法时被阻塞。当一个线程试图进入一个已经被其他线程占用的同步块或同步方法时,会进入 BLOCKED 状态,直到获取到锁为止。
  • WAITING:线程处于等待状态,需要其他线程显式地唤醒。例如调用 Object.wait()Thread.join()LockSupport.park() 方法会使线程进入 WAITING 状态。在这种状态下,线程会释放持有的锁,直到被其他线程调用 Object.notify()Object.notifyAll()LockSupport.unpark() 方法唤醒。
  • TIMED_WAITING:线程处于定时等待状态,在指定的时间后会自动唤醒。例如调用 Thread.sleep(long millis)Object.wait(long timeout)LockSupport.parkNanos() 等方法会使线程进入 TIMED_WAITING 状态。在这种状态下,线程也会释放持有的锁,等待指定的时间后会自动唤醒继续执行。
  • TERMINATED:线程已经执行完毕,生命周期结束。当线程的 run() 方法执行完毕或者抛出未捕获的异常时,线程会进入 TERMINATED 状态,此时线程占用的资源会被释放。

8. 一般线程和守护线程的区别

  • 一般线程:也称为用户线程,是执行用户程序的主要线程。当所有用户线程执行完毕后,Java 虚拟机才会退出。用户线程可以通过继承 Thread 类、实现 Runnable 接口或 Callable 接口来创建。
  • 守护线程:是一种为其他线程提供服务的线程,当所有用户线程执行完毕后,守护线程会自动终止,即使守护线程的任务还没有完成。守护线程通常用于执行一些后台任务,如垃圾回收线程、日志记录线程等。
  • 区别
    1. 生命周期:用户线程会影响 Java 虚拟机的退出,只有当所有用户线程都执行完毕后,Java 虚拟机才会退出;守护线程不会影响 Java 虚拟机的退出,当所有用户线程执行完毕后,守护线程会自动终止。
    2. 设置方式:可以通过 Thread.setDaemon(true) 方法将一个线程设置为守护线程,但必须在调用 start() 方法之前设置,否则会抛出 IllegalThreadStateException 异常。

9. 什么是 sleep wait yield notify notifyAll join,有什么区别

  • sleep
    1. Thread.sleep(long millis)Thread 类的静态方法,用于让当前线程暂停执行指定的时间,线程进入 TIMED_WAITING 状态。在睡眠期间,线程不会释放对象锁,其他线程无法获取该对象的锁。
    2. 例如:

java

public class SleepExample {public static void main(String[] args) {Thread thread = new Thread(() -> {try {System.out.println("Thread is sleeping");Thread.sleep(2000);System.out.println("Thread wakes up");} catch (InterruptedException e) {e.printStackTrace();}});thread.start();}
}

  • wait
    1. Object.wait()Object 类的方法,用于让当前线程进入等待状态,线程进入 WAITINGTIMED_WAITING 状态。当调用 wait() 方法时,线程会释放对象锁,其他线程可以获取该对象的锁。需要其他线程调用 notify()notifyAll() 方法来唤醒等待的线程。
    2. 例如:

java

public class WaitExample {public static void main(String[] args) {final Object lock = new Object();Thread thread1 = new Thread(() -> {synchronized (lock) {try {System.out.println("Thread 1 is waiting");lock.wait();System.out.println("Thread 1 wakes up");} catch (InterruptedException e) {e.printStackTrace();}}});Thread thread2 = new Thread(() -> {synchronized (lock) {System.out.println("Thread 2 is notifying");lock.notify();}});thread1.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}thread2.start();}
}

  • yield
    1. Thread.yield()Thread 类的静态方法,用于让当前线程放弃 CPU 时间片,进入就绪状态,让其他线程有机会执行,但不保证一定会让出 CPU。调用 yield() 方法后,线程不会释放对象锁。
    2. 例如:

java

public class YieldExample {public static void main(String[] args) {Thread thread1 = new Thread(() -> {for (int i = 0; i < 5; i++) {System.out.println("Thread 1: " + i);Thread.yield();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 5; i++) {System.out.println("Thread 2: " + i);}});thread1.start();thread2.start();}
}

  • notify
    1. Object.notify()Object 类的方法,用于唤醒在此对象监视器上等待的单个线程。如果有多个线程在等待,只会随机唤醒一个线程。
  • notifyAll
    1. Object.notifyAll()Object 类的方法,用于唤醒在此对象监视器上等待的所有线程。被唤醒的线程会竞争对象锁,只有获得锁的线程才能继续执行。
  • join
    1. Thread.join()Thread 类的方法,用于等待调用该方法的线程执行完毕。例如,thread.join() 会让当前线程等待 thread 线程执行完毕后再继续执行。
    2. 例如:

java

public class JoinExample {public static void main(String[] args) {Thread thread = new Thread(() -> {try {System.out.println("Thread is running");Thread.sleep(2000);System.out.println("Thread is finished");} catch (InterruptedException e) {e.printStackTrace();}});thread.start();try {thread.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Main thread continues");}
}

10. 什么是中断线程

中断线程是指一个线程向另一个线程发送中断信号,通知它应该停止执行。在 Java 中,可以通过调用 Thread.interrupt() 方法来中断一个线程。被中断的线程可以通过 Thread.interrupted()Thread.isInterrupted() 方法来检查自己是否被中断。

  • Thread.interrupt():用于中断线程,设置线程的中断标志位。当调用 interrupt() 方法时,如果线程处于 WAITINGTIMED_WAITINGSLEEPING 状态,会抛出 InterruptedException 异常,并清除中断标志位;如果线程处于其他状态,只会设置中断标志位。
  • Thread.interrupted():是 Thread 类的静态方法,用于检查当前线程是否被中断,并清除中断标志位。如果当前线程被中断,返回 true,否则返回 false
  • Thread.isInterrupted():是 Thread 类的实例方法,用于检查线程是否被中断,不会清除中断标志位。如果线程被中断,返回 true,否则返回 false

需要注意的是,调用 interrupt() 方法并不会立即终止线程,而是给线程一个中断信号,线程可以根据自己的逻辑来处理这个信号。例如,线程可以在捕获到 InterruptedException 异常时进行相应的处理,或者在适当的时机检查中断标志位,根据标志位的值来决定是否退出线程。

java

public class InterruptExample {public static void main(String[] args) {Thread thread = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("Thread is running");try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 重新设置中断标志位}}System.out.println("Thread is interrupted");});thread.start();try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt();}
}

在这个例子中,线程会不断检查自己的中断标志位,当主线程调用 thread.interrupt() 方法时,线程会捕获到 InterruptedException 异常,然后重新设置中断标志位,最终退出循环。

 友情提示:本文已经整理成文档,可以到如下链接免积分下载阅读

https://download.csdn.net/download/ylfhpy/90498450

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

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

相关文章

【实用部署教程】olmOCR智能PDF文本提取系统:从安装到可视化界面实现

文章目录 引言系统要求1. 环境准备&#xff1a;安装Miniconda激活环境 2. 配置pip源加速下载3. 配置学术加速&#xff08;访问国外资源&#xff09;4. 安装系统依赖5. 安装OLMOCR6. 运行OLMOCR处理PDF文档7. 理解OLMOCR输出结果9. 可视化UI界面9.1 安装界面依赖9.2 创建界面应用…

asp.net core mvc模块化开发

razor类库 新建PluginController using Microsoft.AspNetCore.Mvc;namespace RazorClassLibrary1.Controllers {public class PluginController : Controller{public IActionResult Index(){return View();}} }Views下Plugin下新建Index.cshtml {ViewBag.Title "插件页…

边缘计算革命:重构软件架构的范式与未来

摘要 边缘计算通过将算力下沉至网络边缘&#xff0c;正在颠覆传统中心化软件架构的设计逻辑。本文系统分析了边缘计算对软件架构的范式革新&#xff0c;包括分布式分层架构、实时资源调度、安全防护体系等技术变革&#xff0c;并结合工业物联网、智慧医疗等场景案例&#xff0c…

单链表:数据结构的灵动之链

本文主要讲解链表的概念和结构以及实现单链表 目录 一、链表的概念及结构 二、单链表的实现 1.1链表的实现&#xff1a; 1.2单链表的实现&#xff1a; 单链表尾插&#xff1a; 单链表的头插&#xff1a; 单链表的尾删&#xff1a; 单链表头删&#xff1a; 单链表查找&#…

链表题型-链表操作-JS

一定要注意链表现在的头节点是空节点还是有值的节点。 一、移除链表中的元素 有两种方式&#xff0c;直接使用原来的链表进行删除操作&#xff1b;设置一个虚拟头节点进行删除操作。 直接使用原来的链表进行删除操作时&#xff0c;需要考虑是不是头节点&#xff0c;因为移除…

读《浪潮之巅》:探寻科技产业的兴衰密码

引言&#xff1a;邂逅《浪潮之巅》 在信息技术飞速发展的今天&#xff0c;科技公司如繁星般闪烁&#xff0c;又似流星般划过。而我与《浪潮之巅》的相遇&#xff0c;就像在浩渺的科技海洋中&#xff0c;发现了一座指引方向的灯塔。初次听闻这本书&#xff0c;是在一次技术交流会…

【和春笋一起学C++】文本文件I/O

在windows系统中读取键盘的输入和在屏幕上显示输出统称为&#xff1a;控制台输入/输出。把读取文本文件和把字符输出到文本文件中统称为&#xff1a;文本文件I/O。 目录 1. 输出文本文件 2. 读取文本文件 1. 输出文本文件 把字符输出到文本文件中和输出到控制台很相似&#x…

【C#】WinForm自定义控件及窗体

前言 WinForm&#xff08;Windows Forms&#xff09;是Microsoft.NET框架中的技术&#xff0c;用于开发Windows桌面应用程序。它提供了一套丰富的控件和组件。通过拖放控件、编写事件处理程序等方式快速构建用户界面。 通过属性窗口定制这些控件的外观和行为。 通过数据绑定&am…

Live555+Windows+MSys2 编译Androidso库和运行使用

下载 wget http://www.live555.com/liveMedia/public/live555-latest.tar.gz tar -xzvf live555-latest.tar.gz加入版本控制 git init git add . git commit -a -m "first init" git log修改config.android-arm64 cd live vim config.android-arm64 ./genMakefile…

大模型-提示词工程与架构

什么是提示工程 提示工程&#xff08;Prompt Engineering&#xff09;是一门新兴的技术领域&#xff0c;专注于研究如何设计、构建和优化提示词&#xff0c;以充分发挥大模型的潜力 。它涉及到对语言结构、任务需求、模型特性等多方面因素的综合考量。提示工程的目标是通过精心…

Agent Team 多智能体系统解析

引言 在人工智能技术高速发展的今天&#xff0c;"多智能体协作系统"&#xff08;Agent Team&#xff09;正成为突破效率瓶颈的关键技术。与传统的单体AI不同&#xff0c;这种由多个专业化智能体组成的协同网络&#xff0c;通过分工协作和动态调整&#xff0c;展现出…

【蓝桥杯—单片机】IAP15F2K61S2专项 | 真题整理、解析与拓展 | 省赛题(更新ing...)

IAP15F2K61S2 专项 前言IAP15F2K61S2 介绍&#xff08;基于手册&#xff09;I/O口结构复位管脚RST中断第十四届省赛 外设通过PWM控制第十五届省赛题 性能与工作参数在线调试第十四届省赛题拓展与小结&#xff1a;单片机在线调试常用的接口 功耗第十五届省赛题 前言 在本文中我…

生物化学笔记:医学免疫学原理02 抗原概念+免疫应答+抗原的分类

抗原基本概念 影响抗原刺激机体产生免疫应答的因素 抗原的分类 CG 【北京大学】1080p 王月丹教授 《医学免疫学原理》2022春 全81p

(UI自动化测试)第二篇:元素定位的方法_name定位

二、name定位 ⽅法&#xff1a; driver.find_element_by_name(“name属性值”) 前置&#xff1a; 标签必须name属性 特点&#xff1a; 当前⻚⾯可以重复 提示&#xff1a; 由于name属性值可以重复&#xff0c;所以使⽤时需要查看是否为唯⼀。 # 导包selenium from selenium i…

软考中级-软件设计师 准备

软考中级-软件设计师 准备 一、软考相关1.1、考试时间1.2、考试时长1.3、题型和分值&#xff1a; 二、软考备考2.1、相关书籍2.2、推荐课程&#xff1a;B站up主zst_20012.3、学习路线 一、软考相关 1.1、考试时间 一年有两次软考&#xff0c;一般是五月末和十一月的中旬 以下…

记忆力训练day24

一 数字锁链串联法 数字两位 两位的连

田间机器人幼苗视觉检测与护苗施肥装置研究(大纲)

田间机器人幼苗视觉检测与护苗施肥装置研究 基于多光谱视觉与精准施肥的农业机器人系统设计 第一章 绪论 1.1 研究背景与意义 农业智能化需求&#xff1a; 传统幼苗检测依赖人工&#xff0c;效率低且易遗漏弱苗/病苗施肥不精准导致资源浪费和环境污染 技术挑战&#xff1a;…

Debian12生产环境配置笔记

在 Debian 12 上进行生产环境配置的详细步骤&#xff0c;涵盖软件更新、基础软件安装、Docker 及 Redis 部署&#xff0c;以及 Nginx 配置多个虚拟主机等内容。所有命令均以 root 用户身份执行&#xff0c;无需添加 sudo 1. 更新软件 首先&#xff0c;确保系统上的所有软件包…

HAL库编程知识点---Can.c和Driver_can.c分层开发

在一个工程中&#xff0c;通常会把对CAN外设的操作分成底层和上层两个部分&#xff0c;从而提高代码的模块化和可维护性。一般来说&#xff1a; can.c 通常由硬件抽象层&#xff08;HAL&#xff09;或者自动生成工具&#xff08;如 CubeMX&#xff09;提供或生成。主要负责CAN硬…

7. 【Vue实战--孢子记账--Web 版开发】-- 收支分类设置

本篇文章我们一起来实现收支分类功能。收支分类和前篇文章的主币种设置界面大体类似。我们将详细介绍如何创建和管理不同的收支分类&#xff0c;以便用户可以更好地组织和跟踪他们的财务状况。 一、功能 先来看一下原型界面&#xff0c;界面很简单&#xff0c;这里就不多讲解…