Wend看源码-Java-Executor异步执行器学习

摘要

        本文主要介绍了Java.util.concurrent包所提供的 Executor 异步执行器框架,涵盖了相关的接口和类。

并发执行器类图

图1 java 并发执行器相关类图

Executor 接口

  Executor 接口提供了一种将任务的提交与任务的实际执行机制分离开来的方法。它只有一个方法 execute(Runnable command),用于执行给定的 Runnable 任务。

主要功能
  • 任务提交与执行分离:Executor 允许客户端将任务的提交与任务的实际执行细节分离,使得任务的创建和执行解耦。

  • 线程管理:使用 Executor 可以避免显式地创建和管理线程,而是由 Executor 管理线程的生命周期。

  • 灵活性和可扩展性:Executor 接口可以有不同的实现,比如 ThreadPoolExecutor 或 ScheduledThreadPoolExecutor,这些实现提供了不同的任务执行策略。

使用场景
  • 当需要异步执行任务时。

  • 当需要重用线程而不是为每个任务创建新线程时。

  • 当需要控制并发执行的任务数量时。

  • 当需要执行定时任务时(使用 ScheduledExecutorService)。

ExecutorService 接口

  java.util.concurrent.ExecutorService 扩展了 Executor 接口并提供了管理终止和生成 Future 的方法。ExecutorService 允许你提交可调用的任务(Callable<V>)或运行任务(Runnable),并且可以管理线程池的生命周期。通过使用 ExecutorService,你可以更方便地执行、调度、管理和控制并发任务,而无需直接处理线程创建和销毁等细节。

主要功能
  • 任务提交:允许提交 Runnable 和 Callable 任务,并返回相应的 Future 对象来跟踪任务的状态和结果。

  • 批量提交:支持一次性提交多个任务,并且可以通过 invokeAll() 方法等待所有任务完成,或者通过 invokeAny() 方法等待任意一个任务完成。

  • 线程池管理:提供了一系列的方法来控制线程池的生命周期,包括启动、关闭以及等待所有任务完成。

  • 调度能力:虽然 ScheduledExecutorService 是 ExecutorService 的子接口,专门用于定时和周期性任务的调度,但 ExecutorService 本身也具备基本的调度能力。

AbstractExecutorService

  AbstractExecutorService是 Java 中java.util.concurrent包下的一个抽象类,它实现了ExecutorService接口。ExecutorService接口提供了管理异步任务执行的方法,包括任务提交、任务执行控制和任务结果获取等功能。AbstractExecutorServiceExecutorService接口中的方法提供了默认实现,这使得创建自定义的线程池或者执行服务变得更加方便。

ThreadPoolExecutor

  ThreadPoolExecutor 是一个可扩展的线程池实现,允许你更细粒度地控制线程池的行为。它提供了丰富的构造函数参数,使得你可以根据应用程序的需求来定制线程池的工作方式。

主要特性
  • 核心线程数 (corePoolSize):线程池中保持的最小线程数量,即使这些线程处于空闲状态也不会被终止。

  • 最大线程数 (maximumPoolSize):线程池中允许的最大线程数量。当有新任务提交且当前运行的线程数小于最大值时,线程池可以创建新的线程来处理任务。

  • 保持时间 (keepAliveTime):当线程数超过核心线程数时,多余的空闲线程在终止前等待新任务的最长时间。

  • 工作队列 (BlockingQueue<Runnable>):用于保存等待执行的任务的队列。常见的实现包括 LinkedBlockingQueueSynchronousQueue 等。

  • 线程工厂 (ThreadFactory):用于创建新线程的对象。默认情况下会创建具有默认属性的新线程,但你可以通过提供自定义的 ThreadFactory 来改变这一行为。

  • 拒绝策略 (RejectedExecutionHandler):当线程池无法处理新提交的任务时(例如因为队列已满),所采取的策略。Java 提供了四种内置的拒绝策略,分别是抛出异常、调用者运行、丢弃任务和丢弃最老的任务。

示例代码
import java.util.concurrent.*;public class ThreadPoolExecutorExample {public static void main(String[] args) throws InterruptedException {// 创建一个 ThreadPoolExecutor 实例ThreadPoolExecutor executor = new ThreadPoolExecutor(2, // 核心线程数4, // 最大线程数60L, TimeUnit.SECONDS, // 空闲线程存活时间new LinkedBlockingQueue<>(10), // 工作队列Executors.defaultThreadFactory(), // 线程工厂new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略);// 提交一些任务给线程池for (int i = 0; i < 5; i++) {final int taskNumber = i;executor.submit(() -> {System.out.println("Executing Task " + taskNumber);try {Thread.sleep(1000); // 模拟耗时操作} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}// 关闭线程池executor.shutdown();if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {executor.shutdownNow();}}
}

ScheduledExecutorService 接口

  ScheduledExecutorService 提供了更灵活、强大的调度功能,可以替代 TimerTimerTask。它支持并发执行多个任务,并且提供了更多定制化选项,如固定速率和固定延迟执行等。

使用步骤
  1. 使用 Executors 工厂方法创建一个 ScheduledExecutorService 实例。

  2. 使用 ScheduledExecutorService 的 schedule()scheduleAtFixedRate(), 或 scheduleWithFixedDelay() 方法安排任务执行。

ScheduledThreadPoolExecutor

  ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor,并进一步实现了 ScheduledExecutorService 接口。这意味着除了具备普通线程池的功能外,它还支持定时和周期性任务的调度。这使得它可以用来安排在未来某个时间点执行的任务,或者以固定速率或固定延迟重复执行的任务。

主要特性
  • 调度能力:可以通过 schedule() 方法安排任务在未来某个时间点执行;通过 scheduleAtFixedRate()scheduleWithFixedDelay() 方法安排周期性任务。

  • 继承特性:由于它是 ThreadPoolExecutor 的子类,所以也继承了所有与线程管理相关的配置选项,如核心线程数、最大线程数、工作队列等。

  • 灵活性:虽然 Executors 类提供了简便的方法来创建调度线程池(如 newScheduledThreadPool()),但在某些情况下,直接使用 ScheduledThreadPoolExecutor 可能更加灵活,因为它允许你自定义更多参数。

示例代码
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class ScheduledThreadPoolExecutorExample {public static void main(String[] args) throws InterruptedException {// 创建一个 ScheduledThreadPoolExecutor 实例ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(2);// 安排一个任务,在2秒后执行一次scheduler.schedule(() -> System.out.println("Scheduled Task executed"), 2, TimeUnit.SECONDS);// 安排一个周期性任务,首次执行延迟1秒,之后每隔3秒执行一次scheduler.scheduleAtFixedRate(() -> System.out.println("Periodic Task executed"), 1, 3, TimeUnit.SECONDS);// 让主线程稍微等待一下,以便观察调度任务的输出Thread.sleep(10000);// 关闭调度线程池scheduler.shutdown();if (!scheduler.awaitTermination(60, TimeUnit.SECONDS)) {scheduler.shutdownNow();}}
}

ForkJoinPool

  ForkJoinPool 是 Java 7 引入的一个用于并行执行任务的线程池。它基于工作窃取(Work - Stealing)算法,这使得它在处理可以被递归分解为子任务的计算密集型任务时非常高效。这种任务模型非常适合分治法(Divide - and - Conquer)策略,例如对大型数组进行排序、矩阵运算、树形数据结构的遍历和处理等场景。

工作原理
  • 工作窃取算法

    • ForkJoinPool 中的每个工作线程都有自己的双端队列(Deque)来存放任务。当一个线程完成自己队列中的任务后,它会尝试从其他线程的队列末尾 “窃取” 任务来执行。这种机制充分利用了线程资源,避免了线程空闲,提高了整体的并行效率。

    • 例如,假设有线程 A 和线程 B,线程 A 的任务队列已经为空,而线程 B 的任务队列还有任务。此时线程 A 会尝试从线程 B 的任务队列末尾窃取任务来执行,因为队列末尾的任务通常是新添加的,窃取这些任务对线程 B 正在执行的任务影响较小。

  • 任务分解与合并(Fork 和 Join)

    • Fork 操作是指将一个大任务分解为多个较小的子任务。这些子任务可以被不同的线程并行处理。例如,对于一个计算大型数组部分和的任务,可以将数组分成多个子数组,每个子数组的求和任务作为一个子任务。

    • Join 操作是指等待所有子任务完成,并将子任务的结果合并为最终结果。在上述数组求和的例子中,当各个子数组求和的子任务完成后,需要将这些子和相加得到整个数组的和,这就是 Join 操作。

核心组件和数据结构
  • 线程池(Worker Threads):ForkJoinPool 包含一组工作线程,这些线程用于执行任务。线程数量可以通过构造函数指定,也可以使用默认配置。

  • 任务队列(Work - Queues):每个线程都有自己的任务队列,用于存放分配给该线程的任务以及通过工作窃取获取到的任务。这些队列采用双端队列的形式,方便线程从两端操作任务。

应用场景
  • 大规模数据处理:如对大型数据集进行排序(例如,对一个包含数百万个元素的数组进行快速排序)、数据筛选、数据转换等操作。

  • 递归算法实现:在处理树形结构(如二叉树的遍历、计算树的深度等)或者图结构(如深度优先搜索、广度优先搜索的并行化)的问题时,ForkJoinPool 可以很好地发挥作用。

  • 复杂的数学计算:例如矩阵乘法、大型数值计算等任务,这些任务可以分解为多个子任务进行并行处理。

优势
  • 高效的并行计算:通过工作窃取算法,充分利用多核处理器的性能,提高计算效率,减少任务执行时间。

  • 易于使用:对于可以分解为子任务的问题,使用 ForkJoinPool 和 ForkJoinTask 可以方便地实现并行计算,不需要复杂的线程管理和同步操作。

  • 自动负载均衡:工作窃取机制使得线程之间的工作负载能够自动均衡,避免了某些线程过度忙碌而其他线程空闲的情况。

ExecutorCompletionService

  ExecutorCompletionService 是 Java 并发包中的一个辅助类,它结合了 ExecutorCompletionService 接口的功能。ExecutorCompletionService 提供了一种机制来提交任务并获取它们的结果,同时允许你以非阻塞的方式检查哪些任务已经完成。这使得你可以更灵活地处理并发任务的执行结果,特别是当你需要根据任务完成的顺序来处理结果时。

主要功能
  • 任务提交:你可以通过 submit() 方法向 ExecutorCompletionService 提交可调用的任务(Callable<V>)或运行任务(Runnable)。每个提交的任务都会被包装成一个 Future 对象,并由内部的 Executor 来执行。
  • 结果获取:使用 take()poll() 方法可以从 CompletionService 中取出已完成的任务的结果。take() 会阻塞当前线程直到有任务完成,而 poll() 则是非阻塞的,如果没有任何任务完成则立即返回 null。还有带超时参数的 poll(long timeout, TimeUnit unit) 可以等待指定的时间。
  • 任务状态跟踪:无论任务是以何种顺序完成的,ExecutorCompletionService 都能保证你能够按照完成的顺序来获取任务的结果,而不是按照提交的顺序。
使用场景
  • 当你需要处理大量并发任务,并且希望按照任务完成的顺序来处理结果,而不是按照提交的顺序。

  • 当你需要在所有任务完成之前就开始处理某些已经完成的任务。

  • 当你需要实现一种工作窃取模式,即让空闲的工作线程从其他忙碌的工作线程那里窃取任务来执行。

示例代码
import java.util.concurrent.*;public class ExecutorCompletionServiceExample {public static void main(String[] args) throws InterruptedException, ExecutionException {// 创建一个固定大小为2的线程池ExecutorService executor = Executors.newFixedThreadPool(2);// 创建一个 ExecutorCompletionService 实例ExecutorCompletionService<Integer> ecs = new ExecutorCompletionService<>(executor);// 提交一些任务给 ECSfor (int i = 0; i < 5; i++) {final int taskNumber = i;ecs.submit(() -> {System.out.println("Executing Task " + taskNumber);Thread.sleep((long)(Math.random() * 1000)); // 模拟不同耗时的操作return taskNumber * 2;});}// 获取并处理任务的结果,按照任务完成的顺序for (int i = 0; i < 5; i++) {Future<Integer> future = ecs.take(); // 阻塞直到有任务完成Integer result = future.get(); // 获取任务的结果System.out.println("Task completed with result: " + result);}// 关闭线程池executor.shutdown();if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {executor.shutdownNow();}}
}

        在这个例子中,我们创建了一个 ExecutorCompletionService 实例,并通过它提交了五个不同的任务。然后,我们使用 ecs.take() 方法按照任务完成的顺序来获取每个任务的结果。请注意,即使任务是按顺序提交的,但因为每个任务的执行时间不同,它们可能会以任意顺序完成。

注意事项
  • 资源管理:务必确保在线程池不再需要时调用 shutdown()shutdownNow() 来释放资源。未关闭的线程池可能会导致程序无法正常退出。

  • 异常处理:当使用 submit() 提交 Callable 任务时,任何抛出的异常都会被封装成 ExecutionException 抛出。因此,在调用 get() 方法获取结果时应当做好异常处理。

  • 任务依赖:如果你的任务之间存在依赖关系,考虑使用 CompletableFuture 或者其他高级并发工具来更好地表达这些依赖。

  • 性能优化:选择合适的线程池类型和大小对于性能至关重要。例如,对于 I/O 密集型任务,线程池大小可以设置得相对较大;而对于 CPU 密集型任务,则应保持较小的线程数以避免过多上下文切换带来的开销。

Executors

  Executors提供了一系列工厂方法,用于创建不同类型的 ExecutorServiceScheduledExecutorServiceThreadFactory 和 Callable 对象。这些工厂方法简化了并发编程中常见任务的处理,无需手动实现这些接口。

创建 ExecutorService 实例

        通常,你会使用 Executors 工厂类来创建不同类型的 ExecutorService 实例。以下是几种常见的类型:

  • FixedThreadPool:创建一个固定大小的线程池,其中包含一定数量的线程。当有新任务提交时,如果线程池中有空闲线程,则会立即执行任务;否则,任务将被放入队列中等待。

  • CachedThreadPool:创建一个根据需要创建新线程的线程池,但在可能的情况下会重用已有的空闲线程。适合执行大量短期异步任务。

  • SingleThreadExecutor:创建一个只有一个线程的线程池,确保所有任务都在同一个线程上按顺序执行。

  • ScheduledThreadPool:创建一个支持定时及周期性任务执行的线程池。

  • WorkStealingPool:创建一个基于Fork-Join框架的工作窃取线程池。

示例代码
package person.wend.javalearnexample.util.executor;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class ExecutorsExample {public static void main(String[] args) {// 创建一个可缓存线程池,如果线程空闲60秒后将会被回收ExecutorService cachedThreadPool = Executors.newCachedThreadPool();// 创建一个固定大小的线程池ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);// 创建一个单线程化的线程池ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();// 创建一个支持定时及周期性任务执行的线程池ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);// 创建一个json-task 工作窃取线程池ExecutorService workStealingPool = Executors.newWorkStealingPool();// 使用cachedThreadPool执行任务for (int i = 0; i < 10; i++) {cachedThreadPool.execute(new RunnableTask("CachedThreadPool Task " + i));}// 使用fixedThreadPool执行任务for (int i = 0; i < 5; i++) {fixedThreadPool.execute(new RunnableTask("FixedThreadPool Task " + i));}// 使用singleThreadExecutor执行任务for (int i = 0; i < 3; i++) {singleThreadExecutor.execute(new RunnableTask("SingleThreadExecutor Task " + i));}// 使用scheduledThreadPool执行定时任务for (int i = 0; i < 3; i++) {// 延迟1秒后执行任务scheduledThreadPool.execute(new RunnableTask("ScheduledThreadPool Task " + i));}// 使用 workStealingPool 执行任务for (int i = 0; i < 5; i++) {workStealingPool.execute(new RunnableTask("WorkStealingPool Task " + i));}// 关闭所有线程池shutdownAndAwaitTermination(cachedThreadPool);shutdownAndAwaitTermination(fixedThreadPool);shutdownAndAwaitTermination(singleThreadExecutor);shutdownAndAwaitTermination(scheduledThreadPool);shutdownAndAwaitTermination(workStealingPool);}// 一个简单的Runnable任务static class RunnableTask implements Runnable {private final String taskName;RunnableTask(String taskName) {this.taskName = taskName;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " is executing " + taskName);try {// 模拟任务执行时间TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}// 安全关闭线程池的方法static void shutdownAndAwaitTermination(ExecutorService pool) {pool.shutdown(); // 禁止提交新任务try {// 等待现有任务终止if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {pool.shutdownNow(); // 取消当前执行的任务// 等待任务响应被取消if (!pool.awaitTermination(60, TimeUnit.SECONDS))System.err.println("线程池未正常终止");}} catch (InterruptedException ie) {// 重新取消当前线程进行中断pool.shutdownNow();// 保留中断状态Thread.currentThread().interrupt();}}
}
创建 ScheduledExecutorService
  • newSingleThreadScheduledExecutor:该方法用于创建一个单线程的ScheduledExecutorService。这个单线程可以用于调度任务,使其在给定的延迟之后执行,或者周期性地执行。如果这个单线程在执行任务过程中由于某种故障而终止(在关闭之前),那么在需要执行后续任务时,会有一个新的线程来替代它。并且,任务是保证按顺序执行的,在任何给定时间最多只有一个任务处于活动状态。
  • newScheduledThreadPool:此方法用于创建一个可以调度命令在给定延迟后运行,或者周期性运行的线程池。你可以指定线程池的核心线程数(corePoolSize),这个线程池会根据需要保持一定数量的线程(即使它们处于空闲状态)来处理任务。
示例代码
 public static void createScheduledExecutorService(){ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();// 提交一个延迟2秒后执行的任务executor.schedule(() -> System.out.println("任务执行了"), 2, TimeUnit.SECONDS);// 关闭执行器executor.shutdown();ScheduledExecutorService executor2 = Executors.newScheduledThreadPool(3);// 提交一个延迟2秒后执行的任务executor2.schedule(() -> System.out.println("任务执行了"), 2, TimeUnit.SECONDS);executor2.shutdown();}
创建 ThreadFactory

  我们可以通过Executors.defaultThreadFactory()方法创建一个默认的线程工厂。

 public static void usedDefaultThreadFactory() {// 创建一个Executors.defaultThreadFactoryThreadFactory customThreadFactory = Executors.defaultThreadFactory();// 使用自定义的 ThreadFactory 创建一个 ExecutorServiceExecutorService executorService = Executors.newCachedThreadPool(customThreadFactory);// 提交任务到线程池执行for (int i = 0; i < 10; i++) {executorService.submit(() -> {System.out.println(Thread.currentThread().getName() + " is running.");});}// 关闭线程池executorService.shutdown();}
Callable

  Executors 中包含几个重载的静态方法,用于将 RunnablePrivilegedActionPrivilegedExceptionAction 转换为 Callable 对象。这种转换在需要一个返回结果的任务时非常有用,特别是当你有一个不返回任何结果的 Runnable 任务但希望将其适配到需要 Callable 的上下文中时。

注意事项

        虽然 Executors 类提供了方便的工厂方法,但是在某些情况下,直接使用这些方法可能不是最佳实践。例如,newCachedThreadPool() 和 newFixedThreadPool(int nThreads) 在默认情况下使用的是无界队列,这可能导致内存耗尽问题。因此,在生产环境中,通常建议直接使用 ThreadPoolExecutor 构造函数来创建线程池,以便更精确地控制线程池的行为和性能。

 参考文献/AIGC

通义tongyi.ai_你的全能AI助手-通义千问

豆包

相关文章推荐

         JavaUsefulMode: 基于Java 语言的自定义实用工具集

        JavaUsefulMode是小编编写Java方向学习专栏时的代码示例汇总总结,其中内容包含了该篇文章所涉及的Java代码示例。感兴趣的小伙伴可以直接下载学习。


Wend看源码-Java.util 工具类学习(上)-CSDN博客

Wend看源码-Java.util 工具类学习(下)-CSDN博客

Wend看源码-Java-Collections 工具集学习-CSDN博客

Wend看源码-Java-Arrays 工具集学习-CSDN博客

Wend看源码-Java-fork/Join并行执行任务框架学习-CSDN博客

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

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

相关文章

2025考研江南大学复试科目控制综合(初试807自动控制原理)

​ 2025年全国硕士研究生招生考试江南大学考点 一年年的考研如期而至&#xff0c;我也变成了研二了&#xff0c;作为2次考研经历的学长&#xff0c;总是情不自禁地回想起自己的考研经历&#xff0c;我也会经常从那段经历中汲取力量。我能理解大多数考生考完后的的迷茫无助&…

基于深度学习算法的AI图像视觉检测

基于人工智能和深度学习方法的现代计算机视觉技术在过去10年里取得了显著进展。如今&#xff0c;它被广泛用于图像分类、人脸识别、图像中物体的识别等。那么什么是深度学习&#xff1f;深度学习是如何应用在视觉检测上的呢&#xff1f; 什么是深度学习&#xff1f; 深度学习是…

lec5-传输层原理与技术

lec5-传输层原理与技术 1. 传输层概述 1.1. 关键职责 flow control&#xff0c;流量控制reliability&#xff0c;可靠性 1.2. TCP与UDP对比 面向连接 / 不能连接对数据校验 / 不校验数据丢失重传 / 不会重传有确认机制 / 没有确认滑动窗口流量控制 / 不会流量控制 1.3. 关…

学习C++:数组

数组&#xff1a; 一&#xff0c;概述 所谓数组&#xff0c;就是一个集合&#xff0c;里面存放了相同类型的元素 特点1&#xff1a;数组中的每个数据元素都是相同的数据类型 特点2&#xff1a;数组是由连续的内存位置组成的 二&#xff0c;一维数组 1.一维数组定义方式 三…

Formality:官方Tutorial(一)

相关阅读 Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 本文是对Synopsys Formality User Guide Tutorial中第一个实验的翻译&#xff08;有删改&#xff09;&#xff0c;Lab文件可以从以下链接获取。 Formality官方Tu…

STM32 拓展 RTC(实时时钟)

RTC简介 RTC(Real Time Clock,实时时钟)。是一个掉电后仍然可以继续运行的独立定时器。 RTC模块拥有一个连续计数的计数器,在相应的软件配置下,可以提供时钟日历的功能。修改计数器的值可以重新设置当前时间和日期 RTC还包含用于管理低功耗模式的自动唤醒单元。 RTC实质…

在 macOS 上,你可以使用系统自带的 终端(Terminal) 工具,通过 SSH 协议远程连接服务器

文章目录 1. 打开终端2. 使用 SSH 命令连接服务器3. 输入密码4. 连接成功5. 使用密钥登录&#xff08;可选&#xff09;6. 退出 SSH 连接7. 其他常用 SSH 选项8. 常见问题排查问题 1&#xff1a;连接超时问题 2&#xff1a;权限被拒绝&#xff08;Permission denied&#xff09…

Scrum中敏捷项目经理(Scrum Master)扮演什么角色?

敏捷开发模式已经逐渐被主流的软件研发团队所接受&#xff0c;其中Scrum是最具代表性的敏捷方法之一。Scrum框架中有三个核心角色&#xff1a;Product Owner&#xff08;PO&#xff09;、Scrum Master&#xff08;SM&#xff09;和Development Team&#xff08;DT&#xff09;。…

沙箱模拟支付宝支付3--支付的实现

1 支付流程实现 演示案例 主要参考程序员青戈的视频【支付宝沙箱支付快速集成版】支付宝沙箱支付快速集成版_哔哩哔哩_bilibili 对应的源码在 alipay-demo: 使用支付宝沙箱实现支付功能 - Gitee.com 以下是完整的实现步骤 1.首先导入相关的依赖 <?xml version"1…

Yocto项目 - 详解PACKAGECONFIG机制

引言 Yocto项目是一个强大的嵌入式Linux开发工具&#xff0c;广泛应用于创建定制的嵌入式Linux发行版。在Yocto中&#xff0c;配置和定制化构建系统、软件包、以及生成适用于特定硬件的平台镜像是非常重要的。PACKAGECONFIG是Yocto项目中用于灵活启用或禁用软件包特性的强大工…

【STM32】项目实战——OV7725/OV2604摄像头颜色识别检测(开源)

本篇文章分享关于如何使用STM32单片机对彩色摄像头&#xff08;OV7725/OV2604&#xff09;采集的图像数据进行分析处理&#xff0c;最后实现颜色的识别和检测。 目录 一、什么是颜色识别 1、图像采集识别的一些基本概念 1. 像素&#xff08;Pixel&#xff09; 2. 分辨率&am…

安装PyQt5-tools卡在Preparing metadata (pyproject.toml)解决办法

为了在VS code中使用PyQt&#xff0c;在安装PyQt5-tools时总卡在如下这一步 pyqt5 Preparing metadata (pyproject.toml)经过各种尝试&#xff0c;最终问题解决&#xff0c;在此记录方法。 首先进入PyQt5-tools官网查看其适配的Python版本&#xff0c;网址如下&#xff1a; h…

RAG实战:本地部署ragflow+ollama(linux)

1.部署ragflow 1.1安装配置docker 因为ragflow需要诸如elasticsearch、mysql、redis等一系列三方依赖&#xff0c;所以用docker是最简便的方法。 docker安装可参考Linux安装Docker完整教程&#xff0c;安装后修改docker配置如下&#xff1a; vim /etc/docker/daemon.json {…

56.在 Vue 3 中使用 OpenLayers 通过 moveend 事件获取地图左上和右下的坐标信息

前言 在现代 Web 开发中&#xff0c;地图应用越来越成为重要的组成部分。OpenLayers 是一个功能强大的 JavaScript 地图库&#xff0c;它提供了丰富的地图交互和操作功能&#xff0c;而 Vue 3 是当前流行的前端框架之一。在本篇文章中&#xff0c;我们将介绍如何在 Vue 3 中集…

Codigger集成Copilot:智能编程助手

在信息技术的快速发展中&#xff0c;编程效率和创新能力的提升成为了开发者们追求的目标。Codigger平台通过集成Copilot智能编程助手&#xff0c;为开发者提供了一个强大的工具&#xff0c;以增强其生产力、创新力和技能水平。本文将深入探讨Codigger与Copilot的集成如何为IT专…

用uniapp写一个播放视频首页页面代码

效果如下图所示 首页有导航栏&#xff0c;搜索框&#xff0c;和视频列表&#xff0c; 导航栏如下图 搜索框如下图 视频列表如下图 文件目录 视频首页页面代码如下 <template> <view class"video-home"> <!-- 搜索栏 --> <view class…

Java高频面试之SE-08

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本牛马baby今天又来了&#xff01;哈哈哈哈哈嗝&#x1f436; 成员变量和局部变量的区别有哪些&#xff1f; 在 Java 中&#xff0c;成员变量和局部变量是两种不同类型的变量&#xff0c;它们在作用域…

在Typora中实现自动编号

文章目录 在Typora中实现自动编号1. 引言2. 准备工作3. 自动编号的实现3.1 文章大纲自动编号3.2 主题目录&#xff08;TOC&#xff09;自动编号3.3 文章内容自动编号3.4 完整代码 4. 应用自定义CSS5. 结论 在Typora中实现自动编号 1. 引言 Typora是一款非常流行的Markdown编辑…

Oracle exp和imp命令导出导入dmp文件

目录 一. 安装 instantclient-tools 工具包二. exp 命令导出数据三. imp 命令导入数据四. expdp 和 impdp 命令 一. 安装 instantclient-tools 工具包 ⏹官方网站 https://www.oracle.com/cn/database/technologies/instant-client/linux-x86-64-downloads.html ⏹因为我们在…

小程序发版后,强制更新为最新版本

为什么要强制更新为最新版本&#xff1f; 在小程序的开发和运营过程中&#xff0c;强制用户更新到最新版本是一项重要的策略&#xff0c;能够有效提升用户体验并保障系统的稳定性与安全性。以下是一些主要原因&#xff1a; 1. 功能兼容 新功能或服务通常需要最新版本的支持&…