线程池详解:在SpringBoot中的最佳实践
线程池详解:在SpringBoot中的最佳实践
引言
在Java并发编程中,线程池是一种非常重要的资源管理工具,它允许我们在应用程序中有效地管理和重用线程,从而提高性能并降低资源消耗。特别是在SpringBoot等企业级应用中,正确使用线程池对于应用程序的稳定性和性能至关重要。
根据阿里巴巴《Java开发手册》中的强制要求:
【强制要求】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors返回的线程池对象的弊端如下:
1) FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2)CachedThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
本文将详细介绍线程池的基本原理、常用的工作队列类型及其优缺点,以及在SpringBoot中的简单实现方式。
线程池的基本原理
线程池的核心思想是复用线程,避免频繁创建和销毁线程所带来的性能开销。它的工作流程如下:
- 当有新任务提交时,线程池会判断当前运行的线程数是否小于核心线程数(corePoolSize),如果是,则创建新线程执行任务。
- 如果当前运行的线程数等于或大于核心线程数,则将任务放入工作队列。
- 如果工作队列已满,且当前线程数小于最大线程数(maximumPoolSize),则创建新线程执行任务。
- 如果工作队列已满,且当前线程数等于或大于最大线程数,则根据拒绝策略处理该任务。
┌─────────────────┐ ┌───────────────┐ ┌─────────────────┐│ │ │ │ │ ││ corePoolSize │─────────│ workQueue │─────────│ maximumPoolSize││ 核心线程数 │ │ 工作队列 │ │ 最大线程数 │└─────────────────┘ └───────────────┘ └─────────────────┘│ │ ││ │ │▼ ▼ ▼┌─────────────────┐ ┌───────────────┐ ┌─────────────────┐│ 创建新线程执行 │ │ 放入工作队列 │ │ 创建新线程执行 │└─────────────────┘ └───────────────┘ └─────────────────┘││▼┌─────────────────┐│ 拒绝策略 │└─────────────────┘
两次创建线程对比:
| 对比维度 | 第一次创建(核心线程) | 第二次创建(非核心线程) |
|---|---|---|
| 触发条件 | 线程数 < corePoolSize | 线程数 ≥ corePoolSize 且 队列已满 |
| 线程性质 | 核心线程,默认长期存活 | 临时线程,空闲超时后被回收 |
| 目的 | 维持基础并发能力 | 应对突发流量,防止队列积压 |
是否受keepAliveTime影响 | 默认否(需设置allowCoreThreadTimeOut=true) | 是 |
ThreadPoolExecutor的主要参数
ThreadPoolExecutor构造函数有7个参数:
public ThreadPoolExecutor(int corePoolSize, // 核心线程数int maximumPoolSize, // 最大线程数long keepAliveTime, // 空闲线程存活时间TimeUnit unit, // 时间单位BlockingQueue<Runnable> workQueue, // 工作队列ThreadFactory threadFactory, // 线程工厂RejectedExecutionHandler handler // 拒绝策略
)
参数详解
- corePoolSize:核心线程数,线程池中会维持的最小线程数,即使它们处于空闲状态。
- maximumPoolSize:最大线程数,线程池允许创建的最大线程数。
- keepAliveTime:空闲线程的存活时间,当线程数大于核心线程数时,多余的空闲线程存活的最长时间。
- unit:keepAliveTime的时间单位。
- workQueue:工作队列,用于存放待执行的任务。常用的有:
- ArrayBlockingQueue:基于数组的有界阻塞队列,按FIFO排序。
- LinkedBlockingQueue:基于链表的阻塞队列,按FIFO排序,容量可选,如不指定则为Integer.MAX_VALUE。
- SynchronousQueue:不存储元素的阻塞队列,插入操作必须等待另一个线程的删除操作。
- PriorityBlockingQueue:具有优先级的无界阻塞队列。
- threadFactory:线程工厂,用于创建新线程,可以自定义线程的名称、优先级等。
- handler:拒绝策略,当工作队列已满且线程数达到maximumPoolSize时,如何处理新提交的任务。常用的有:
- AbortPolicy:直接抛出RejectedExecutionException异常(默认)。
- CallerRunsPolicy:由提交任务的线程自己执行该任务。
- DiscardPolicy:直接丢弃任务,不抛出异常。
- DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务。
工作队列(WorkQueue)类型及优缺点
选择合适的工作队列对线程池的性能影响很大。以下是常用的几种队列类型及其优缺点:
1. ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO(先进先出)原则对元素进行排序。
优点:
- 有界队列,可以防止资源耗尽
- 内存占用固定
- 适合已知任务量的场景
缺点:
- 队列容量一旦设定,无法动态调整
- 当队列满时,新任务可能会被拒绝
- 对于突发流量不够灵活
2. LinkedBlockingQueue
基于链表的阻塞队列,按FIFO原则对元素进行排序。
优点:
- 链表结构,动态分配内存
- 可以指定容量,也可以不指定(默认为Integer.MAX_VALUE)
- 吞吐量通常高于ArrayBlockingQueue
缺点:
- 如果不指定容量,可能导致OOM(阿里巴巴手册中提到的问题)
- 每个节点都会占用更多的内存(节点对象的开销)
3. SynchronousQueue
不存储元素的阻塞队列,每个插入操作必须等待另一个线程的删除操作。
优点:
- 直接传递,没有队列容量限制的概念
- 适合任务处理速度快、不需要队列缓冲的场景
- 可以避免队列中任务的积压
缺点:
- 没有存储能力,任何时候都无法插入元素,除非有另一个线程正在取出元素
- 如果没有足够的线程来处理任务,新任务可能会被拒绝
- 通常需要较大的最大线程数来配合使用
4. PriorityBlockingQueue
具有优先级的无界阻塞队列,元素按优先级顺序出队。
优点:
- 可以按任务优先级执行
- 适合有任务优先级区分的场景
缺点:
- 无界队列,可能导致OOM
- 优先级比较会带来额外的性能开销
5. DelayQueue
延迟队列,元素只有到了指定的延迟时间才能被取出。
优点:
- 适合需要延时处理的任务
- 可以实现定时任务的功能
缺点:
- 无界队列,可能导致OOM
- 时间依赖性高
SpringBoot中的线程池配置
在SpringBoot应用中配置线程池有多种方式,下面介绍几种常用的方法:
1. 使用@Bean注解创建线程池
@Configuration
public class ThreadPoolConfig {@Beanpublic ThreadPoolExecutor threadPoolExecutor(@Value("${thread.pool.corePoolSize:10}") int corePoolSize,@Value("${thread.pool.maxPoolSize:20}") int maxPoolSize,@Value("${thread.pool.queueCapacity:200}") int queueCapacity,@Value("${thread.pool.keepAliveSeconds:60}") int keepAliveSeconds) {// 使用有界队列BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(queueCapacity);// 自定义线程工厂ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("业务处理线程-%d").setDaemon(false).setPriority(Thread.NORM_PRIORITY).build();return new ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveSeconds,TimeUnit.SECONDS,workQueue,threadFactory,new ThreadPoolExecutor.CallerRunsPolicy());}
}
2. 使用ThreadPoolTaskExecutor(Spring提供的线程池封装)
@Configuration
public class ThreadPoolConfig {@Beanpublic ThreadPoolTaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 核心线程数executor.setCorePoolSize(10);// 最大线程数executor.setMaxPoolSize(20);// 队列容量executor.setQueueCapacity(200);// 线程最大空闲时间executor.setKeepAliveSeconds(60);// 线程名前缀executor.setThreadNamePrefix("taskExecutor-");// 拒绝策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 等待所有任务完成后再关闭线程池executor.setWaitForTasksToCompleteOnShutdown(true);// 等待终止的时间executor.setAwaitTerminationSeconds(60);executor.initialize();return executor;}
}
3. 使用@Async注解进行异步调用
首先配置异步执行的线程池:
@Configuration
@EnableAsync
public class AsyncConfig {@Bean("asyncExecutor")public Executor asyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(10);executor.setMaxPoolSize(20);executor.setQueueCapacity(200);executor.setKeepAliveSeconds(60);executor.setThreadNamePrefix("Async-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}
}
然后在需要异步执行的方法上添加@Async注解:
@Service
public class EmailService {@Async("asyncExecutor")public CompletableFuture<Boolean> sendEmail(String to, String subject, String content) {// 发送邮件的耗时操作return CompletableFuture.completedFuture(Boolean.TRUE);}
}
实际应用场景
1. 批量处理任务
在需要处理大量数据的场景中,可以使用线程池进行并行处理:
@Service
public class BatchProcessService {@Autowiredprivate ThreadPoolTaskExecutor taskExecutor;public void processBatch(List<Data> dataList) {// 分批处理int batchSize = 100;for (int i = 0; i < dataList.size(); i += batchSize) {List<Data> batch = dataList.subList(i, Math.min(i + batchSize, dataList.size()));taskExecutor.submit(() -> processBatchInternal(batch));}}private void processBatchInternal(List<Data> batch) {// 处理单个批次的数据batch.forEach(data -> {// 处理单条数据});}
}
2. 异步通知
在完成某些操作后需要进行异步通知时:
@Service
public class NotificationService {@Autowiredprivate ThreadPoolExecutor threadPoolExecutor;public void sendNotifications(List<String> userIds, String message) {for (String userId : userIds) {threadPoolExecutor.execute(() -> {try {// 发送通知System.out.println("向用户 " + userId + " 发送通知: " + message);} catch (Exception e) {// 错误处理System.err.println("发送通知失败: " + e.getMessage());}});}}
}
3. 定时任务
结合SpringBoot的@Scheduled注解使用自定义线程池:
@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {@Autowiredprivate ThreadPoolTaskExecutor taskExecutor;@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar) {taskRegistrar.setScheduler(scheduledTaskExecutor());}@Bean(destroyMethod = "shutdown")public Executor scheduledTaskExecutor() {return Executors.newScheduledThreadPool(10, r -> {Thread t = new Thread(r);t.setName("scheduled-task-" + t.getId());return t;});}
}@Component
public class DataSyncTask {@Autowiredprivate ThreadPoolTaskExecutor taskExecutor;@Scheduled(cron = "0 0/30 * * * ?") // 每30分钟执行一次public void syncData() {System.out.println("开始数据同步任务...");// 获取需要同步的数据列表List<String> dataIds = getDataIdsToSync();// 使用线程池并行处理for (String dataId : dataIds) {taskExecutor.submit(() -> syncSingleData(dataId));}}private List<String> getDataIdsToSync() {// 获取需要同步的数据ID列表return Arrays.asList("data1", "data2", "data3");}private void syncSingleData(String dataId) {try {System.out.println("同步数据: " + dataId);// 具体同步逻辑...} catch (Exception e) {System.err.println("数据同步失败: " + e.getMessage());}}
}
线程池监控
在生产环境中,监控线程池的运行状态是非常重要的,可以帮助我们及时发现问题并进行调整。
1. 自定义监控指标
@Component
@RequiredArgsConstructor
public class ThreadPoolMonitor {private final ThreadPoolExecutor threadPoolExecutor;private final ThreadPoolTaskExecutor taskExecutor;@Scheduled(fixedRate = 60000) // 每分钟记录一次public void monitorThreadPool() {ThreadPoolExecutor executor = threadPoolExecutor;logThreadPoolStatus("自定义线程池", executor);// 监控ThreadPoolTaskExecutorThreadPoolExecutor tpExecutor = taskExecutor.getThreadPoolExecutor();logThreadPoolStatus("任务执行线程池", tpExecutor);}private void logThreadPoolStatus(String poolName, ThreadPoolExecutor executor) {int activeCount = executor.getActiveCount(); // 活跃线程数int poolSize = executor.getPoolSize(); // 当前线程数int corePoolSize = executor.getCorePoolSize(); // 核心线程数int maximumPoolSize = executor.getMaximumPoolSize(); // 最大线程数long completedTaskCount = executor.getCompletedTaskCount(); // 已完成任务数long taskCount = executor.getTaskCount(); // 总任务数int queueSize = executor.getQueue().size(); // 队列大小log.info("线程池状态 [{}]: 活跃线程数={}, 线程池大小={}, 核心线程数={}, " +"最大线程数={}, 已完成任务数={}, 总任务数={}, 队列中任务数={}, 队列剩余容量={}",poolName, activeCount, poolSize, corePoolSize, maximumPoolSize,completedTaskCount, taskCount, queueSize, (executor.getQueue() instanceof LinkedBlockingQueue)? ((LinkedBlockingQueue<?>) executor.getQueue()).remainingCapacity(): -1);// 计算线程池利用率double utilizationRate = (double) activeCount / poolSize;log.info("线程池 [{}] 利用率: {}", poolName, String.format("%.2f%%", utilizationRate * 100));// 监控任务队列使用情况if (executor.getQueue() instanceof LinkedBlockingQueue) {LinkedBlockingQueue<?> queue = (LinkedBlockingQueue<?>) executor.getQueue();int capacity = queue.size() + queue.remainingCapacity();double queueUsageRate = (double) queueSize / capacity;log.info("队列 [{}] 使用率: {}", poolName, String.format("%.2f%%", queueUsageRate * 100));}// 任务拒绝情况监控(需要自定义RejectedExecutionHandler来记录拒绝次数)if (executor.getRejectedExecutionHandler() instanceof MonitoredRejectedExecutionHandler) {MonitoredRejectedExecutionHandler handler = (MonitoredRejectedExecutionHandler) executor.getRejectedExecutionHandler();log.info("线程池 [{}] 任务拒绝次数: {}", poolName, handler.getRejectedCount());}}// 自定义的拒绝策略处理器,增加了拒绝次数的记录public static class MonitoredRejectedExecutionHandler implements RejectedExecutionHandler {private final RejectedExecutionHandler delegate;private final AtomicLong rejectedCount = new AtomicLong(0);public MonitoredRejectedExecutionHandler(RejectedExecutionHandler delegate) {this.delegate = delegate;}@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {rejectedCount.incrementAndGet();delegate.rejectedExecution(r, executor);}public long getRejectedCount() {return rejectedCount.get();}}
}
线程池参数选择的经验法则
合理配置线程池参数是很重要的,以下是一些经验法则:
-
核心线程数的选择:
- CPU密集型任务:通常设置为CPU核心数 + 1
- IO密集型任务:可以设置为CPU核心数 * 2
// 获取CPU核心数 int processors = Runtime.getRuntime().availableProcessors(); // CPU密集型任务 int corePoolSize = processors + 1; // IO密集型任务 int ioPoolSize = processors * 2; -
队列容量的选择:
- 要考虑内存资源限制
- 考虑任务的平均执行时间
- 考虑系统的负载能力
-
拒绝策略的选择:
- 一般推荐使用CallerRunsPolicy,它不会丢弃任务,而是将任务回退给调用者
- 对于不重要的任务,可以使用DiscardPolicy直接丢弃
常见问题与解决方案
1. 任务执行慢,队列堆积
问题:任务执行速度慢,导致队列中堆积了大量任务。
解决方案:
- 增加核心线程数和最大线程数
- 优化任务执行逻辑,提高处理速度
- 使用更合适的队列类型,如优先级队列
2. 频繁触发拒绝策略
问题:经常有任务被拒绝执行。
解决方案:
- 增加队列容量
- 增加最大线程数
- 实现更合理的拒绝策略
- 添加任务提交速率限制
3. OOM问题
问题:使用无界队列导致内存溢出。
解决方案:
- 使用有界队列,如ArrayBlockingQueue或指定容量的LinkedBlockingQueue
- 监控队列大小,在达到警戒值时采取措施
总结
线程池是Java并发编程中非常重要的工具,正确使用线程池可以提高应用程序的性能和稳定性。在SpringBoot应用中,我们应该遵循阿里巴巴Java开发手册的建议,避免使用Executors创建线程池,而是通过ThreadPoolExecutor明确指定各项参数。
选择合适的工作队列类型、设置合理的线程数量和队列容量,以及实现适当的拒绝策略,这些都是使用线程池时需要考虑的关键因素。通过本文介绍的简单配置方法,你可以在SpringBoot应用中轻松实现一个高效且安全的线程池。
相关文章:
线程池详解:在SpringBoot中的最佳实践
线程池详解:在SpringBoot中的最佳实践 引言 在Java并发编程中,线程池是一种非常重要的资源管理工具,它允许我们在应用程序中有效地管理和重用线程,从而提高性能并降低资源消耗。特别是在SpringBoot等企业级应用中,正…...
扩展卡尔曼滤波
1.非线性系统的线性化 标准卡尔曼滤波 适用于线性化系统,扩展卡尔曼滤波 则扩展到了非线性系统,核心原理就是将非线性系统线性化,主要用的的知识点是 泰勒展开(我另外一篇文章的链接),如下是泰勒展开的公式…...
AI作为学术评审专家有哪些优缺点?
大家好这里是AIWritePaper官方账号,官网👉AIWritePaper论文完成初稿之后,一般情况下,宝子们还需要找专家给我们提出评审意见。找专家评审其实并不容易,即使对老师来说,找人评审论文也是一件苦活。我们这个时…...
微信小程序登录和获取手机号
目录 准备工作 实现流程 实现代码 公共部分 通过code获取openid等信息 解密手机号 扩展 不借助工具类实现解密 借助工具类获取access_token 准备工作 需要小程序账号(可以去微信公众平台创建一个测试号或者正式号) appid:小程序id …...
4.Matplotlib:基础绘图
一 直方图 1.如何构建直方图 将值的范围分段,将整个值的范围分成一系列间隔,然后计算每个间隔中有多少值。 2.直方图的适用场景 一般用横轴表示数据类型,纵轴表示分布情况。 直方图可以用于识别数据的分布模式和异常值,以及观察数…...
与Aspose.pdf类似的jar库分享
如果你在寻找类似于 Aspose.PDF 的 JAR 库,这些库通常用于处理 PDF 文档的创建、编辑、转换、合并等功能。以下是一些类似的 Java 库,它们提供 PDF 处理的功能,其中一些是收费的,但也有开源选项: 1. iText (iText PDF…...
VSCode 市场发现恶意扩展正在传播勒索软件!
在VSCode 市场中发现了两个隐藏着勒索软件的恶意扩展。其中一个于去年 10 月出现在微软商店,但很长时间没有引起注意。 这些是扩展ahban.shiba 和 ahban.cychelloworld,目前已从商店中删除。 此外,ahban.cychelloworld 扩展于 2024 年 10 月…...
工作流引擎Flowable介绍及SpringBoot整合使用实例
Flowable简介 Flowable 是一个轻量级的业务流程管理(BPM)和工作流引擎,基于 Activiti 项目发展而来,专注于提供高性能、可扩展的工作流解决方案。它主要用于企业级应用中的流程自动化、任务管理和审批流等场景。 Flowable 的核心…...
K8s证书--运维之最佳选择(K8s Certificate - the best Choice for Operation and Maintenance)
K8s证书--运维之最佳选择 No -Number- 01 一个月速通CKA 为了速通CKA,主要办了两件事情 1. 在官方的Killercoda上,练习CKA的题目。把命令敲熟悉。 // https://killercoda.com/killer-shell-ckad 2. 使用K3s在多台虚拟机上快速搭建了K8s集群&…...
MySQL 8.0.41源码目录深度解析:探索数据库内核的架构蓝图
文章目录 MySQL 8.0.41源码目录深度解析:探索数据库内核的架构蓝图一、MySQL 8.0.41 目录结构总览1.1 安装目录核心子目录1.2 数据目录关键组件 二、核心源码模块剖析2.1 SQL 引擎核心(sql / 目录)2.1.1 核心组件2.1.2 架构亮点 2.2 存储引擎…...
Leaflet.js+leaflet.heat实现热力图
Leaflet热力图 #mermaid-svg-I1zXN0OrNCBGKEWy {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-I1zXN0OrNCBGKEWy .error-icon{fill:#552222;}#mermaid-svg-I1zXN0OrNCBGKEWy .error-text{fill:#552222;stroke:#5522…...
通过git文件查看大模型下载链接的解决方案
大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...
多源最短路:Floyd算法の暴力美学
多源最短路求解的是图中的任意两个节点之间的最短路。 前文我们已经讲过单源最短路,我们完全可以做n次单源最短路算法,求出任意两节点的最短距离。最快的堆优化版的 dijkstra 算法的时间复杂度为o(m * logm),枚举n次时…...
初教六双机一飞冲天动作要领
初教六双机一飞冲天动作要领 初教六双机“一飞冲天”是典型的垂直爬升特技动作,要求双机以近乎垂直的姿态同步高速爬升,展现飞机的动力性能与编队协同能力。以下是该动作的详细技术解析与执行要点: 一、动作定义与特点 基本形态 双机以相同速…...
qtcore在docker容器中运行
FROM ubuntu # 设置时区环境变量 ENV TZAsia/Shanghai RUN echo "${TZ}" > /etc/timezone \ && ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \ && apt update \ && apt install -y tzdata \ && rm -rf /var/lib/apt…...
simpleITK - Setup - Pythonic Syntactic Sugar
Pythonic Syntactic Sugar Image Basics Notebook 非常简单,与 ITK 的 C 接口非常接近。 Sugar非常棒,它能让你精力充沛,更快地完成任务!SimpleITK 也应用了大量Sugar来帮助更快地完成任务。 %matplotlib inline import matplo…...
【leetcode hot 100 215】数组中的第K个最大元素
解法一:维护最大最小值 -> 堆 -> k个元素的最小值堆 class Solution {public int findKthLargest(int[] nums, int k) {// 维护最大最小值 -> 堆 -> k个元素的最小值堆PriorityQueue<Integer> heap new PriorityQueue<>((n1, n2) -> n…...
下载vmware17
我用VMware10安装ubuntu24,死活不能成功,要么突然退出,要么装着装着,眼看完成,居然卡住不动,一查日志,提示光盘读取失败(用的ISO文件,居然装模作样的说光驱读取失败&…...
德昂观点:如何看待MicroStrategy改名为Strategy?
2025年2月,纳斯达克上市公司MicroStrategy(股票代码:MSTR)宣布更名为“Strategy”,并同步启用全新品牌标识与橙色主视觉。这不仅是品牌形象的更新,更是公司战略方向的明确宣示。德昂作为MSTR中国区BI合作伙…...
嵌入式八股RTOS与Linux---网络系统篇
前言 关于计网的什么TCP三次握手 几层模型啊TCP报文啥的不在这里讲,会单独分成一个计算机网络模块 这里主要介绍介绍lwip和socket FreeRTOS下的网络接口–移植LWIP 实际上FreeRTOS并不自带网络接口,我们一般会通过移植lwip协议栈让FreeRTOS可以通过网络接口收发数据,具体可…...
Django 生成 ssl 安全证书,切换 https、wss协议(daphne 、nginx)
Django 普通 http 协议不够安全,无法支持连接本地摄像头(虽然在本地 localhost 上能连),此时需要切换成 https 协议(先提个醒,我这个方法最后失败了,不过对您应该也有帮助) 目录 配置…...
告别Win10强制更新:永久关闭系统更新指南
你是否厌倦了Win10在开关机时的强制自动更新?无论你是在赶时间还是专注于工作,那突如其来的更新提示总是让人不胜其烦。屏幕上那句“正在更新,请勿关闭电源”的提示,仿佛是对你无奈的嘲笑。别担心,今天我将教你如何永久…...
【django】2-1 (django配置) 应用配置、中间件配置、模板配置
文章目录 1 基本设置2 应用配置2.1 django核心应用2.2 常用第三方应用 3 中间件3.1 默认使用的中间件3.2 其它内置中间件3.3 第三方中间件3.4 中间件的执行顺序 4 模板引擎配置4.1 配置字典的键4.2 上下文处理器 创建django项目后,会自动生成初始的项目文件如下&…...
nginx-rtmp-module之ngx_rtmp.c代码详解
1. ngx_rtmp.c — RTMP模块的主逻辑实现 这个文件是 RTMP 模块的核心,包含了 RTMP 协议模块的初始化、配置解析和服务端口的管理等功能。它的主要职责是处理 RTMP 配置、初始化模块、配置事件、初始化 RTMP 端口等。 主要功能和逻辑: 模块初始化 (ngx_…...
罗杰斯特回归
定义 逻辑回归其实就是原来的线性回归加了激活函数,这个函数其实就是sigmoid函数,把一个回归的连续数值压缩到了0到1的空间,其实只要有函数能够满足把数值压缩到0,1之间就可以(因为0到1之间的数值就是概率值) 对于分类…...
Android 10.0 SystemUI状态栏去掉刘海屏功能实现
1.前言 在android10.0的系统rom定制化开发中,在一些产品中,对于带有刘海屏的产品中,会因为 刘海屏导致状态栏能显示图片的位置很小,然后会出现状态栏图标显示为白点的功能, 接下来看下问题怎么解决 2.SystemUI状态栏去掉刘海屏功能实现的核心类 frameworks/base/core/r…...
三维空间中点、线、面的关系
三维空间中点、线、面的关系 点相对于平面的位置关系直线相对于平面的位置关系1.根据三点计算平面方程 //根据3点计算平面方程#include <iostream> #include <cmath> #include <vector>...
【嵌入式学习2】C语言 - VScode环境搭建
目录 ## 语言分类 ## c语言编译器 ## VScode相关配置 ## 语言分类 编译型语言:C,C解释型语言:python,JS ## c语言编译器 分类GCC 系列MinGWCygwinMSVC系列一套编程语言编译器将GCC编译器和GNU Binutils移植到Win32平台下的产物…...
TCP/IP的网络连接设备
TCP/IP层物理层网卡、集线器、中继器数据链路层网桥、交换机网络层路由器传输层网关应用层 1.网桥:网桥主要功能是将一个网络的数据沿通信线路复制到另一个网络中去,可以有效的连接两个局域网 2.网关:网关又称协议转换器,是将两…...
蓝桥杯真题 2109.统计子矩阵
原题地址:1.统计子矩阵 - 蓝桥云课 问题描述 给定一个 NMNM 的矩阵 AA, 请你统计有多少个子矩阵 (最小 1111, 最大 NM)NM) 满足子矩阵中所有数的和不超过给定的整数 KK ? 输入格式 第一行包含三个整数 N,MN,M 和 KK. 之后 NN 行每行包含 MM 个整数, 代表矩阵 AA. 输出格…...
