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

自定义线程池-初识

自定义线程池-初步了解

创建一个固定大小的线程池

  • 在Java中,你可以通过自定义线程池并指定线程的名称来实现你的需求。下面是一个简单的示例,展示了如何创建一个固定大小的线程池,并给每个线程指定一个名称:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class CustomThreadPool {public static void main(String[] args) {// 创建一个固定大小的线程池int poolSize = 10;ExecutorService executor = Executors.newFixedThreadPool(poolSize);// 为线程池中的每个线程指定名称for (int i = 0; i < poolSize; i++) {executor.execute(() -> {Thread.currentThread().setName("Thread-" + i);// 这里可以执行你的任务代码});}// 关闭线程池(这不会立即关闭,而是等待所有任务都完成)executor.shutdown();}
}
  • 在上述代码中,我们使用了Executors.newFixedThreadPool来创建一个固定大小的线程池。然后,我们使用一个循环来为线程池中的每个线程执行任务,并在任务中为每个线程指定一个名称。最后,我们调用executor.shutdown()来关闭线程池。

  • 请注意,线程的名称只是用于标识和调试目的,并不会影响线程的行为。另外,如果你想在任务执行期间获取线程的名称,你需要在适当的地方(例如在任务代码中)使用Thread.currentThread().getName()来获取它。

自定义ThreadPoolExecutor

  • ThreadPoolExecutor是Java中的一个线程池实现,它提供了创建和管理线程池的功能。你可以通过继承ThreadPoolExecutor来自定义一个线程池。

以下是一个自定义的ThreadPoolExecutor的示例代码,它设置了线程池的基本属性和参数:

import java.util.concurrent.*;public class CustomThreadPoolExecutor extends ThreadPoolExecutor {public CustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);}// 重写方法,自定义线程池的行为@Overrideprotected void beforeExecute(Thread t, Runnable r) {// 在任务执行前执行的代码System.out.println("Before executing task: " + r.toString());}@Overrideprotected void afterExecute(Runnable r, Throwable t) {// 在任务执行后执行的代码System.out.println("After executing task: " + r.toString());}@Overrideprotected void terminated() {// 线程池终止时执行的代码System.out.println("ThreadPoolExecutor terminated.");}
}

让我们详细解释一下每个方法和参数的作用:

  1. CustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue):这是构造函数,用于初始化线程池的基本属性。
  2. corePoolSize:线程池的核心线程数。即使线程处于空闲状态,也会保留的线程数。
  3. maximumPoolSize:线程池允许的最大线程数。当队列满了,并且已有线程数小于核心线程数,则创建新的线程执行任务。
  4. keepAliveTime:线程池中超过额定大小的线程在关闭之前等待新任务的最长时间。这里使用TimeUnit来指定时间单位。
  5. unit:keepAliveTime的时间单位。
  6. workQueue:用于存放等待执行的任务的队列。当线程池中的线程数超过核心线程数时,新任务会被放入此队列等待执行。
  7. beforeExecute(Thread t, Runnable r):在任务r开始执行之前调用的方法。可以在此添加自定义的预处理逻辑。
  8. afterExecute(Runnable r, Throwable t):在任务r执行结束之后调用的方法。可以在此添加自定义的清理逻辑。
  9. terminated():当ThreadPoolExecutor被终止时调用的方法。可以在此添加自定义的终止逻辑。

通过重写这些方法,你可以在任务执行的生命周期的不同阶段添加自定义的行为,以满足你的需求。

示例

上面自定义的CustomThreadPoolExecutor是一个线程池的实现,你可以通过创建一个CustomThreadPoolExecutor对象来创建一个线程池,并使用该线程池来执行任务。

以下是一个使用CustomThreadPoolExecutor的示例代码:

import java.util.concurrent.*;public class CustomThreadPoolExecutorExample {public static void main(String[] args) {// 创建一个CustomThreadPoolExecutor对象CustomThreadPoolExecutor customThreadPoolExecutor = new CustomThreadPoolExecutor(2, // 核心线程数4, // 最大线程数60L, // 保持活跃时间TimeUnit.SECONDS, // 时间单位new LinkedBlockingQueue<Runnable>() // 等待队列);// 提交任务给线程池执行for (int i = 0; i < 6; i++) {customThreadPoolExecutor.execute(new Task(i));}// 关闭线程池customThreadPoolExecutor.shutdown();}
}class Task implements Runnable {private int taskId;public Task(int taskId) {this.taskId = taskId;}@Overridepublic void run() {System.out.println("Task " + taskId + " is running.");}
}

在这个示例中,我们首先创建了一个CustomThreadPoolExecutor对象,并设置了它的核心线程数、最大线程数、保持活跃时间和等待队列。然后,我们通过调用execute()方法向线程池提交了6个任务。每个任务是一个实现了Runnable接口的Task对象,它的run()方法只是简单地输出一个消息。最后,我们调用shutdown()方法关闭线程池。

在任务执行过程中,CustomThreadPoolExecutor会自动管理线程的创建和销毁,并保证任务按照提交的顺序执行。同时,我们还重写了beforeExecute()afterExecute()terminated()方法,分别在任务执行前、执行后和线程池终止时执行自定义的逻辑。例如,在beforeExecute()方法中,我们可以输出任务的信息,以便观察任务的执行顺序。

ThreadPoolExecutor中有以下几个重要的指标1:

  • 核心线程数(corePoolSize):线程池中的常驻核心线程数,即使没有任务需要执行,核心线程也不会被回收。当有新任务提交时,如果核心线程都在忙碌,则会创建新的线程来处理任务。
  • 最大线程数(maximumPoolSize):线程池能够容纳同时执行的最大线程数。当工作队列满了并且活动线程数达到最大线程数时,如果还有新任务提交,线程池将创建新的线程来处理任务。但是超过最大线程数的线程可能会导致资源消耗过大。
  • 空闲线程存活时间(keepAliveTime):空闲线程存活时间指的是非核心线程在没有任务执行时的最长存活时间。当线程池中的线程数超过核心线程数且空闲时间达到设定值时,多余的线程将被终止,直到线程池中的线程数不超过核心线程数。
  • 时间单位(unit):时间单位是用于表示核心线程数和空闲线程存活时间的单位。常见的时间单位包括秒、毫秒、分钟等。
  • 工作队列(workQueue):用于存储待执行的任务。当线程池中的线程都在忙碌时,新提交的任务将被添加到工作队列中等待执行。
  • 线程工厂(threadFactory):用于创建新线程。线程工厂提供了创建线程的方法,可以自定义线程的名称、优先级等属性。
  • 拒绝策略(rejectedExecutionHandler):定义了当线程池无法接受新任务时的处理策略。当工作队列已满且线程池中的线程数已达到最大线程数时,新任务将被拒绝执行。

在运行过程中,要监控线程池中线程的各个指标,需要监控以下指标:

  • 核心线程数:监控常驻核心线程数。
  • 最大线程数:监控能够容纳同时执行的最大线程数。
  • 当前线程数:监控当前正在执行任务的线程数。
  • 队列中任务数:监控工作队列中待执行的任务数。
  • 已完成任务数:监控已经执行完成的任务数。
  • 总任务数:监控总共提交的任务数。
  • 线程空闲时间:监控线程在没有任务执行时的空闲时间。
  • 线程异常数:监控线程执行任务时抛出的异常数。

要获取线程池中线程的各个指标,可以通过以下方法:

  1. 使用线程池自带的监控工具,例如 ThreadPoolExecutor 中的 ThreadPoolExecutor.getPoolSize(), ThreadPoolExecutor.getActiveCount(), ThreadPoolExecutor.getTaskCount(), ThreadPoolExecutor.getCompletedTaskCount() 等方法。
  2. 使用线程池中的线程的 API,例如 Thread.getState(), Thread.getName(), Thread.getStackTrace() 等方法获取线程的各个状态信息。
  3. 使用第三方监控工具,例如 JMX、VisualVM 等工具来监控线程池的状态信息。
  4. 自定义监控程序,通过编写程序来获取线程池的状态信息,例如通过网络连接、文件读取等方式获取线程池的状态信息。

要编写一套详细的自定义监控程序,需要完成以下步骤:

  1. 确定监控指标:根据需求确定需要监控的指标,例如线程池中的核心线程数、最大线程数、当前线程数、队列中任务数、已完成任务数、总任务数、线程空闲时间、线程异常数等。
  2. 设计数据采集方式:根据监控指标,设计数据采集方式,例如编写程序通过调用线程池的 API 来获取状态信息,或者通过网络连接、文件读取等方式获取线程池的状态信息。
  3. 编写数据采集程序:根据设计的数据采集方式,编写程序实现数据采集功能。
  4. 存储数据:将采集到的数据存储到数据库或文件中,以便后续分析。
  5. 分析数据:对采集到的数据进行统计分析,例如计算平均响应时间、成功率等指标,并根据分析结果提供相应的建议或警告。
  6. 展示数据:将分析后的数据通过图表或报告等方式展示出来,以便用户直观地了解线程池的状态信息。

下面是一个简单的示例程序,用于监控线程池的状态信息:

import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;public class ThreadPoolMonitor implements Runnable {private ThreadPoolExecutor threadPool;private long interval;private TimeUnit unit;public ThreadPoolMonitor(ThreadPoolExecutor threadPool, long interval, TimeUnit unit) {this.threadPool = threadPool;this.interval = interval;this.unit = unit;}@Overridepublic void run() {while (true) {// 获取线程池状态信息int corePoolSize = threadPool.getCorePoolSize();int maximumPoolSize = threadPool.getMaximumPoolSize();int activeCount = threadPool.getActiveCount();int completedTaskCount = threadPool.getCompletedTaskCount();int taskCount = threadPool.getTaskCount();long keepAliveTime = threadPool.getKeepAliveTime(TimeUnit.MILLISECONDS);TimeUnit unit = threadPool.getKeepAliveTimeUnit();String workQueue = threadPool.getQueue().getClass().getName();ThreadFactory threadFactory = threadPool.getThreadFactory();RejectedExecutionHandler rejectedExecutionHandler = threadPool.getRejectedExecutionHandler();// 输出状态信息System.out.println(new Date() + " CorePoolSize: " + corePoolSize + ", MaximumPoolSize: " + maximumPoolSize +", ActiveCount: " + activeCount + ", CompletedTaskCount: " + completedTaskCount + ", TaskCount: " + taskCount +", KeepAliveTime: " + keepAliveTime + " " + unit + ", WorkQueue: " + workQueue + ", ThreadFactory: " + threadFactory +", RejectedExecutionHandler: " + rejectedExecutionHandler);try {// 休眠一段时间,等待下一次采集Thread.sleep(interval);} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);ThreadPoolMonitor monitor = new ThreadPoolMonitor(threadPool, 1000, TimeUnit.MILLISECONDS);new Thread(monitor).start();}
}

要获取达到最大线程数后被拒绝的线程数或异常数,可以通过线程池的 getRejectedExecutionHandler() 方法获取 RejectedExecutionHandler 对象,然后根据具体情况实现自定义的拒绝策略。

Java 中提供了几种默认的拒绝策略:

  1. ThreadPoolExecutor.AbortPolicy: 默认的拒绝策略,直接抛出 RejectedExecutionException 异常。
  2. ThreadPoolExecutor.CallerRunsPolicy: 调用者线程运行被拒绝的任务,不抛出异常。
  3. ThreadPoolExecutor.DiscardPolicy: 忽略被拒绝的任务,不保存,也不抛出异常。
  4. ThreadPoolExecutor.DiscardOldestPolicy: 丢弃队列中最老的任务,然后尝试重新提交。
  5. ThreadPoolExecutor.WaitForTasksPolicy: 等待当前任务队列的任务都完成后再执行被拒绝的任务。

ThreadPoolExecutor.CallerRunsPolicy

  • 如果以上策略都不满足需求,可以自定义实现 RejectedExecutionHandler 接口,根据具体需求进行处理。例如,可以统计被拒绝的线程数或异常数,或者将任务保存到数据库或文件等地方,以便后续处理。

  • ThreadPoolExecutor.CallerRunsPolicy是Java线程池中的一个拒绝策略,当线程池拒绝一个新任务时,它会调用该策略来处理被拒绝的任务。

  • 当拒绝策略为CallerRunsPolicy时,线程池会调用执行者所在的线程来执行被拒绝的任务,也就是说,直接在调用execute方法的线程中运行被拒绝的任务。如果执行程序已关闭,则会丢弃该任务。这种策略会降低对于新任务提交速度,影响程序的整体性能。如果您的应用程序可以承受此延迟并且要求任何一个任务请求都要被执行,可以选择该策略。

以下是一个简单的示例代码,演示如何获取达到最大线程数后被拒绝的线程数:

import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.RejectedExecutionHandler;public class ThreadPoolMonitor implements Runnable {private ThreadPoolExecutor threadPool;private long interval;private TimeUnit unit;public ThreadPoolMonitor(ThreadPoolExecutor threadPool, long interval, TimeUnit unit) {this.threadPool = threadPool;this.interval = interval;this.unit = unit;}@Overridepublic void run() {while (true) {// 获取线程池状态信息int corePoolSize = threadPool.getCorePoolSize();int maximumPoolSize = threadPool.getMaximumPoolSize();int activeCount = threadPool.getActiveCount();int completedTaskCount = threadPool.getCompletedTaskCount();int taskCount = threadPool.getTaskCount();long keepAliveTime = threadPool.getKeepAliveTime(TimeUnit.MILLISECONDS);TimeUnit unit = threadPool.getKeepAliveTimeUnit();String workQueue = threadPool.getQueue().getClass().getName();ThreadFactory threadFactory = threadPool.getThreadFactory();RejectedExecutionHandler rejectedExecutionHandler = threadPool.getRejectedExecutionHandler();int rejectedCount = 0; // 被拒绝的线程数if (rejectedExecutionHandler instanceof ThreadPoolExecutor.AbortPolicy) {rejectedCount = ((ThreadPoolExecutor.AbortPolicy) rejectedExecutionHandler).getRejectedExecutionCount();} else if (rejectedExecutionHandler instanceof ThreadPoolExecutor.CallerRunsPolicy) {rejectedCount = ((ThreadPoolExecutor.CallerRunsPolicy) rejectedExecutionHandler).getRunCount();} else if (rejectedExecutionHandler instanceof ThreadPoolExecutor.DiscardPolicy) {rejectedCount = ((ThreadPoolExecutor.DiscardPolicy) rejectedExecutionHandler).getDiscardCount();} else if (rejectedExecutionHandler instanceof ThreadPoolExecutor.DiscardOldestPolicy) {rejectedCount = ((ThreadPoolExecutor.DiscardOldestPolicy) rejectedExecutionHandler).getDiscardCount();} else if (rejectedExecutionHandler instanceof ThreadPoolExecutor.WaitForTasksPolicy) {rejectedCount = ((ThreadPoolExecutor.WaitForTasksPolicy) rejectedExecutionHandler).getTaskCount();}System.out.println(new Date() + " CorePoolSize: " + corePoolSize + ", MaximumPoolSize: " + maximumPoolSize +", ActiveCount: " + activeCount + ", CompletedTaskCount: " + completedTaskCount + ", TaskCount: " + taskCount +", KeepAliveTime: " + keepAliveTime + " " + unit + ", WorkQueue: " + workQueue + ", ThreadFactory: " + threadFactory +", RejectedExecutionHandler: " + rejectedExecutionHandler + ", RejectedCount: " + rejectedCount);try {// 休眠一段时间,等待下一次采集Thread.sleep(interval);} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);ThreadPoolMonitor monitor = new ThreadPoolMonitor(threadPool, 1000, TimeUnit.MILLISECONDS}

下面是一个关于线程池中线程各个指标的含义的表格:

指标含义
maximumPoolSize最大线程数,当线程池繁忙时最多可以拥有的线程数
corePoolSize核心线程数,线程池中始终保持的线程数
minimumPoolSize最小线程数,线程池中线程数的下限值
poolSize当前线程数,线程池中正在运行的线程数
activeCount活动线程数,当前正在执行任务的线程数
taskCount任务数,线程池中已提交的任务总数
completedTaskCount完成的任务数,线程池中已经完成的任务总数
largestPoolSize最大的线程数,线程池曾拥有的最大线程数
keepAliveTime空闲线程存活时间,当线程超过该时间没有任务时,就会被回收
TimeUnit时间单位,用于描述参数3的单位,如秒、毫秒等
BlockingQueue任务队列,用于保存待执行任务的容器
ThreadFactory线程工厂,用于创建线程池中线程的工厂方法,可以通过它来设置线程的命名规则、优先级和线程类型
RejectedExecutionHandler拒绝策略,当任务量超过线程池可以保存的最大任务数时,执行的策略

CompletableFuture+ThreadPoolThread实现并发异步处理

CompletableFuture 是 Java 8 引入的一个强大的并发工具,它允许你以异步的方式处理任务,并且可以方便地组合多个异步任务。ThreadPoolExecutor 则是 Java 提供的一个线程池工具,它可以根据需要创建和管理线程,以支持并发执行任务。

下面是一个使用 CompletableFuture 和 ThreadPoolExecutor 实现并发异步处理的示例:

import java.util.concurrent.*;public class CompletableFutureWithThreadPool {public static void main(String[] args) {// 创建一个固定大小的线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(5, // 核心线程数10, // 最大线程数60, // 线程空闲超过60秒则销毁TimeUnit.SECONDS, // 时间单位new LinkedBlockingQueue<Runnable>() // 任务队列);// 使用 CompletableFuture 来执行异步任务CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {// 模拟一个耗时操作try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}return "任务1完成";}, executor);CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {// 模拟一个耗时操作try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}return "任务2完成";}, executor);CompletableFuture<Void> all = CompletableFuture.allOf(future1, future2);all.thenRun(() -> {System.out.println("所有任务都已完成");executor.shutdown(); // 关闭线程池});}
}
  • 在上面的示例中,我们首先创建了一个 ThreadPoolExecutor,并指定了其核心线程数、最大线程数、线程空闲超时时间以及任务队列。然后,我们使用 CompletableFuture.supplyAsync 方法来执行两个异步任务,这两个任务会使用我们创建的线程池来执行。每个异步任务都是一个耗时操作,模拟了某些需要长时间运行的任务。最后,我们使用 CompletableFuture.allOf 方法来等待两个异步任务都完成,当所有任务都完成后,我们输出一条消息并关闭线程池。

  • 通过这个示例,你可以看到如何使用 CompletableFuture 和 ThreadPoolExecutor 来实现并发异步处理。你可以根据实际需求调整线程池的参数以及异步任务的逻辑。

注意

  • 是的,对于一直在运行的线上业务流程,通常不需要显式地关闭线程池。线程池会自动管理线程的生命周期,根据需要创建和销毁线程。当提交任务到线程池时,线程池会根据其配置的参数决定是否创建新的线程来执行任务。如果当前线程数已经达到池中线程数的上限,则任务会等待,直到有空闲线程可用。

  • 一般情况下,线程池会一直运行,直到没有任务提交或者显式地关闭线程池。即使没有任务提交,线程池中的线程也不会立即被销毁,而是会保持空闲状态,等待新的任务到来。

  • 然而,需要注意的是,长时间运行的线程池可能会占用系统资源,并且如果存在长时间运行的任务,可能会阻塞其他任务的执行。因此,在设计和使用线程池时,应该根据业务需求和系统资源情况进行合理的配置和监控,以确保系统的稳定性和性能。

  • 如果你的程序一直在运行,并且线程池已经调用了shutdown方法,那么线程池中的线程将不再接受新的任务。此时,如果你尝试提交新的任务给线程池,将会抛出RejectedExecutionException异常。

  • 如果你希望在程序运行时能够暂停线程池的执行,而不是完全关闭线程池,你可以使用线程池的shutdown或shutdownNow方法。这两个方法都会停止接受新的任务,但shutdownNow方法会尝试停止所有正在执行的任务,而shutdown方法则会等待已提交的任务执行完毕后再停止。

  • 如果你希望在程序运行时能够动态地调整线程池的大小或改变线程池的行为,可以考虑使用可扩展的线程池或配置更灵活的线程池实现,例如ForkJoinPool或ExecutorService的定制实现。这些线程池可以更好地适应不同的应用程序需求。

果你的程序一直在运行,并且线程池已经调用了shutdown方法,那么线程池中的线程将不再接受新的任务。此时,如果你尝试提交新的任务给线程池,将会抛出RejectedExecutionException异常。

  • 如果你希望在程序运行时能够暂停线程池的执行,而不是完全关闭线程池,你可以使用线程池的shutdown或shutdownNow方法。这两个方法都会停止接受新的任务,但shutdownNow方法会尝试停止所有正在执行的任务,而shutdown方法则会等待已提交的任务执行完毕后再停止。

  • 如果你希望在程序运行时能够动态地调整线程池的大小或改变线程池的行为,可以考虑使用可扩展的线程池或配置更灵活的线程池实现,例如ForkJoinPool或ExecutorService的定制实现。这些线程池可以更好地适应不同的应用程序需求。

相关文章:

自定义线程池-初识

自定义线程池-初步了解 创建一个固定大小的线程池 在Java中&#xff0c;你可以通过自定义线程池并指定线程的名称来实现你的需求。下面是一个简单的示例&#xff0c;展示了如何创建一个固定大小的线程池&#xff0c;并给每个线程指定一个名称&#xff1a; import java.util.…...

低代码平台:IVX 重新定义编程

目录 &#x1f36c;一、写在前面 &#x1f36c;二、低代码平台是什么 &#x1f36c;三、为什么程序员和技术管理者不太可能接受“低代码”平台&#xff1f; &#x1f36d;1、不安全&#xff08;锁定特性&#xff09; &#x1f36d;2、不信任 &#x1f36c;四、IVX低代码平台 &a…...

Android之自定义时间选择弹框

文章目录 前言一、效果图二、实现步骤1.自定义Dialog2.xml布局3.背景白色转角drawable4.取消按钮背景drawable5.确定按钮背景drawable6.NumberPicker样式和弹框样式7.弹框动画8.Activity使用 总结 前言 随着产品人员不断变态下&#xff0c;总是会要求我们的界面高大上&#xf…...

异地容灾系统和数据仓库系统设计和体系结构

&#xff08; 1&#xff09;生产系统数据同步到异地容灾系统 生产系统与异地容灾系统之间是通过百兆网连接的&#xff1b;生产系统的数据库是 Oracle 9i RAC&#xff0c;总的数据量大约为 3 TB&#xff0c;涉及五千多张表。对这些表进行分析归 类&#xff0c;发现容灾系统真正…...

【pytest】tep环境变量、fixtures、用例三者之间的关系

tep是一款测试工具&#xff0c;在pytest测试框架基础上集成了第三方包&#xff0c;提供项目脚手架&#xff0c;帮助以写Python代码方式&#xff0c;快速实现自动化项目落地。 在tep项目中&#xff0c;自动化测试用例都是放到tests目录下的&#xff0c;每个.py文件相互独立&…...

风控引擎如何快速添加模型,并实时了解运行状态?

目录 风控模型的主要类型 风控引擎如何管理模型&#xff1f; 模型就是基于目标群体的大规模采样数据&#xff0c;挖掘出某个实际问题或客观事物的现象本质及运行规律&#xff0c;利用抽象的概念分析存在问题或风险&#xff0c;计算推演出减轻、防范问题或风险的对策过程&…...

一文读懂|内核顺序锁

Linux 内核有非常多的锁机制&#xff0c;如&#xff1a;自旋锁、读写锁、信号量和 RCU 锁等。本文介绍一种和读写锁比较相似的锁机制&#xff1a;顺序锁&#xff08;seqlock&#xff09;。 顺序锁与读写锁一样&#xff0c;都是针对多读少写且快速处理的锁机制。而顺序锁和读写…...

openproject在docker下的安装

官方指引&#xff1a;https://www.openproject.org/docs/installation-and-operations/installation/docker/ 网友指引&#xff1a;https://blog.csdn.net/joefive/article/details/119409550 建个自己的数据文件夹&#xff1a; sudo mkdir -p /var/lib/openproject/{mydata…...

React【React是什么?、创建项目 、React组件化、 JSX语法、条件渲染、列表渲染、事件处理】(一)

文章目录 React是什么&#xff1f; 为什么要学习React React开发前准备 创建React项目 React项目结构简介 React组件化 初识JSX 渲染JSX描述的页面 JSX语法 JSX的Class与Style属性 JSX生成的React元素 条件渲染&#xff08;一&#xff09; 条件渲染 &#xff0…...

Ubuntu系统下配置 Qt Creator 输入中文、配置软件源的服务器地址、修改Ubuntu系统时间

上篇介绍了Ubuntu系统下搭建QtCreator开发环境。我们可以发现安装好的QtCreator不能输入中文&#xff0c;也没有中文输入法供选择&#xff0c;这里需要进行设置。 文章目录 1. 配置软件源的服务器地址2. 先配置Ubuntu系统语言&#xff0c;设置为中文3. 安装Fcitx插件&#xff…...

Ab3d.PowerToys 11.0.8614 Crack

版本 11.0.8614 修补程序 使用 MouseCameraController 移动相机时防止旋转 FreeCamera。 版本 11.0.8585 重大更改&#xff1a;由于专利问题删除了 ViewCubeCameraController - 请联系支持人员以获取更多信息以及如果您想继续使用此控件。添加了 CameraNavigationCircles 控件…...

汽车3D HMI图形引擎选型指南【2023】

推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 2002年&#xff0c;电影《少数派报告》让观众深入了解未来。 除了情节的核心道德困境之外&#xff0c;大多数人都对它的技术着迷。 我们看到了自动驾驶汽车、个性化广告和用户可以无缝交互的 3D 计算机界面。 令人惊讶的是…...

Stable Diffusion stable-diffusion-webui开发笔记

https://lexica.art/ lexica.art 该网站拥有数百万Stable Diffusion案例的文字描述和图片&#xff0c;可以为大家提供足够的创作灵感。可以提供promt灵感 https://civitai.com/ Civitai是一个聚集AI绘图爱好者的社区&#xff0c;在此网站上有许多定制化的模型&#xff0c;特…...

利用MQ实现mysql与elasticsearch数据同步

流程 1.声明exchange、queue、RoutingKey 2. 在hotel-admin中进行增删改&#xff08;SQL&#xff09;&#xff0c;完成消息发送 3. 在hotel-demo中完成消息监听&#xff0c;并更新elasticsearch数据 4. 测试同步 1.引入依赖 <!--amqp--> <dependency><groupId&…...

linux免密登录最简单--图文详解

最简单的免密登录 1.A电脑生成秘钥 ssh-keygen -t rsa 2.A电脑将秘钥传给B电脑 ssh-copy-id root192.168.1.129 #将秘钥直接传给B电脑 需要输入B电脑的密码&#xff0c;可以看到成功。 3.测试 同理&#xff1a;如果B->A也需要免密登录&#xff0c;统一的操作。 大功告…...

HTTP/1.1协议中的请求报文

2023年8月30日&#xff0c;周三上午 目录 概述请求报文示例详述 概述 HTTP/1.1协议的请求报文由以下几个部分组成&#xff1a; 请求行&#xff08;Request Line&#xff09;请求头部&#xff08;Request Headers&#xff09;空行&#xff08;Blank Line&#xff09;请求体&a…...

攻防世界-Hear-with-your-Eyes

原题 解题思路 是一个没有后缀的文件&#xff0c;题目提示要用眼睛看这段音频&#xff0c;notepad打开文件&#xff0c;没什么东西。 加后缀zip再解压看看。 使用Audacity打开音频文件...

ZED相机获取图像python

import pyzed.sl as sl import cv2 import numpy as np import osclass CameraZed2:def __init__(self,resolutionNone,fps30,depthMode None):self.zed sl.Camera()self.input_type sl.InputType()self.init_params sl.InitParameters(input_tself.input_type)# 设置分辨率…...

Oracle系列之--Profile

Oracle系列之--Profile_oracle profile_楼兰过客的博客-CSDN博客...

学习Bootstrap 5的第四天

目录 表格 基础表格 实例 条纹表格 实例 带边框表格 实例 有悬停效果的行 实例 黑色/深色表格 实例 黑色/深色条纹表格 实例 可悬停的黑色/深色表格 实例 无边框表格 实例 上下文类 可用的上下文类&#xff1a; 实例 表头颜色 实例 小型表格 实例 响应…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

LabVIEW双光子成像系统技术

双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制&#xff0c;展现出显著的技术优势&#xff1a; 深层组织穿透能力&#xff1a;适用于活体组织深度成像 高分辨率观测性能&#xff1a;满足微观结构的精细研究需求 低光毒性特点&#xff1a;减少对样本的损伤…...

华为OD机试-最短木板长度-二分法(A卷,100分)

此题是一个最大化最小值的典型例题&#xff0c; 因为搜索范围是有界的&#xff0c;上界最大木板长度补充的全部木料长度&#xff0c;下界最小木板长度&#xff1b; 即left0,right10^6; 我们可以设置一个候选值x(mid)&#xff0c;将木板的长度全部都补充到x&#xff0c;如果成功…...

mac:大模型系列测试

0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何&#xff0c;是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试&#xff0c;是可以跑通文章里面的代码。训练速度也是很快的。 注意…...

goreplay

1.github地址 https://github.com/buger/goreplay 2.简单介绍 GoReplay 是一个开源的网络监控工具&#xff0c;可以记录用户的实时流量并将其用于镜像、负载测试、监控和详细分析。 3.出现背景 随着应用程序的增长&#xff0c;测试它所需的工作量也会呈指数级增长。GoRepl…...