Spring线程池优雅关闭
前言
线程池大家一定不陌生,常被用来异步执行一些耗时的任务。但是线程池如何优雅的关闭,却少有人关注。
当 JVM 进程关闭时,你提交到线程池的任务会被如何处理?如何保证任务不丢?
ThreadPoolExecutor
Java 对线程池的封装是 ThreadPoolExecutor,它的三个核心方法,和线程的优雅关闭息息相关。
1、shutdown
关闭线程池,拒绝接收新任务,已经提交的任务会继续执行。方法会立即返回,并不会阻塞线程直到任务执行完毕。
2、shutdownNow
立即关闭线程池,拒绝接收新任务,尝试中断正在执行的任务,并且返回所有未执行的任务,方法也会立即返回。注意,如果执行中的任务不响应中断,任务可能永远无法停止。
3、awaitTermination
等待线程池终止,直到所有任务执行完毕或者超时。如果线程池终止(关闭且任务执行完)返回 true,超时则返回 false。
要想让线程池优雅关闭,需要解决两个问题:
- 知道何时关闭线程池
- 知道如何正确关闭线程池
第一个问题,一般是在程序退出时关闭线程池,最常见的做法,就是运行时注册钩子函数,当 JVM 进程退出时,会自动触发我们自定义的钩子函数,在函数里面正确关闭线程池即可。
第二个问题,如何正确关闭线程池呢?毫无疑问,首先是让线程池不再接收新的任务。其次,针对已经提交到线程池里的任务,有两种处理方式:
- 把这些已提交的任务执行完,这必然会延长程序关闭的时间
- 取消执行队列中的任务,中断正在执行的任务
可以发现,这两种处理方式其实就分别对应了 shutdown() 和 shutdownNow() 方法。
第2种方式会丢任务,这通常不是我们想要的结果。第1种方式也有它的问题,如果任务非常多或者执行非常耗时,程序就会一直关不掉,这也不是我们想要的结果。此时就需要一个平衡,一般的做法是:先拒绝接收新的任务,然后给定一个超时时间,在这个时间内尽可能的把任务执行完,超时以后如果还有没执行的任务,就先把任务相关的数据保存下来,后面可以重新执行,尽量保证不丢任务。
下面是一个简单示例:
public class ThreadPoolGracefulShutdown {final static ExecutorService executorService = Executors.newSingleThreadExecutor();@RequiredArgsConstructorpublic static class Task implements Runnable {final Integer bizId;@Overridepublic void run() {Threads.sleep(1000);System.out.println("task" + bizId + " completed");}}static void registerShutdownHook() {Runtime.getRuntime().addShutdownHook(new Thread() {@SneakyThrows@Overridepublic void run() {System.err.println("shutdown hook...");executorService.shutdown();//不再接收新的任务,等待已提交的任务执行完if (executorService.awaitTermination(5L, TimeUnit.SECONDS)) {System.err.println("任务执行完,正常关闭");} else {List<Integer> bizIds = executorService.shutdownNow().stream().map(i -> ((Task) i).bizId).collect(Collectors.toList());System.err.println("任务保存,后续执行:" + bizIds);}}});}public static void main(String[] args) {// 注册钩子函数registerShutdownHook();// 提交任务for (int i = 1; i <= 10; i++) {executorService.execute(new Task(i));}// 2秒后kill掉当前进程new Timer(true).schedule(new TimerTask() {@Overridepublic void run() {Systems.killSelf();}}, 2000L);}
}
代码解释,一个单线程的线程池,每个任务执行1秒,一次性注册10个任务。2秒后会自动kill掉当前进程,钩子函数开始执行,关闭线程池并等待5秒让任务继续执行,超时后把没执行的任务数据保存下来,以便后续执行。最终程序运行输出结果:
task1 completed
task2 completed
shutdown hook...
task3 completed
task4 completed
task5 completed
task6 completed
task7 completed
任务保存,后续执行:[8, 9, 10]
Spring线程池优雅关闭
在 Spring 环境下,线程池通常不需要我们手动关闭,Spring 对 ThreadPoolExecutor 做了二次封装,把线程池转化成 bean 进行全生命周期的管理。那么,在 Spring 环境下,线程池又该如何优雅关闭呢?
Spring 对线程池的封装类是 ThreadPoolTaskExecutor,一般用法是定义一个线程池的配置类,然后声明线程池的 bean 交给 Spring 管理。如下示例,声明了一个单线程的线程池,Spring 环境启动时会自动创建线程池,并在程序退出时自动关闭线程池。
@Configuration
public class ThreadPoolConfig {@Bean("taskExecutor")public ThreadPoolTaskExecutor threadPoolTaskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setThreadNamePrefix("TASK-");executor.setCorePoolSize(1);executor.setMaxPoolSize(1);executor.setQueueCapacity(100);executor.setKeepAliveSeconds(60);executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());return executor;}
}
线程池的自动创建和关闭,得益于 Spring Bean 的生命周期管理。ThreadPoolTaskExecutor 实现了 InitializingBean 和 DisposableBean 接口,线程池会在 Bean 初始化时创建,在 Bean 销毁时关闭。
Bean 初始化时,最终会调用ThreadPoolTaskExecutor#initializeExecutor
,Spring 会根据配置创建 ThreadPoolExecutor
Bean 销毁时,最终会调用ExecutorConfigurationSupport#shutdown
,Spring 会根据配置判断是等待任务执行完还是取消执行。
Spring 线程池优雅关闭的两个重要配置:
- waitForTasksToCompleteOnShutdown 是否等待任务执行
- awaitTerminationSeconds 等待线程池终止的秒数
如果 waitForTasksToCompleteOnShutdown = false,Spring 会调用 shutdownNow() 立即关闭线程池,取消任务执行。如果 waitForTasksToCompleteOnShutdown = true,Spring 会调用 shutdown() 继续执行任务,并等待 awaitTerminationMillis 秒数,超时以后,任务还是会丢掉。
看一个示例,配置一个单线程的线程池,关闭时最多等待3秒执行剩下的任务
@Configuration
public class ThreadPoolConfig {@Bean("taskExecutor")public ThreadPoolTaskExecutor threadPoolTaskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setThreadNamePrefix("TASK-");executor.setCorePoolSize(1);executor.setMaxPoolSize(1);executor.setQueueCapacity(100);executor.setKeepAliveSeconds(60);executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());executor.setWaitForTasksToCompleteOnShutdown(true);executor.setAwaitTerminationSeconds(3);return executor;}
}
Spring 环境启动后,立即提交10个任务,每个任务执行1秒钟
@Component
@RequiredArgsConstructor
public class TaskRunner implements CommandLineRunner {private final Log log = LogFactory.getLog("TaskRunner");private final ThreadPoolTaskExecutor taskExecutor;@Overridepublic void run(String... args) throws Exception {for (int i = 0; i < 10; i++) {final int index = i + 1;taskExecutor.submit(() -> {try {Threads.sleep(1000L);log.info("task" + index + " completed");} catch (Exception e) {e.printStackTrace();}});}}
}
启动 Spring 应用,并在2秒后kill掉进程。经过我们分析,最终应该只会有5个任务被执行。运行,发现确实如此。
2025-01-03 16:15:01 , TASK-1 , TaskRunner , task1 completed
killSelf...
2025-01-03 16:15:02 , TASK-1 , TaskRunner , task2 completed
2025-01-03 16:15:03 , TASK-1 , TaskRunner , task3 completed
2025-01-03 16:15:04 , TASK-1 , TaskRunner , task4 completed
2025-01-03 16:15:05 , TASK-1 , TaskRunner , task5 completed
2025-01-03 16:15:05 , SpringApplicationShutdownHook , o.s.s.c.ThreadPoolTaskExecutor , Timed out while waiting for executor 'taskExecutor' to terminate
需要注意的是,awaitTerminationSeconds 必须设置,不然 Spring 只调用 shutdown() 是非阻塞的,没有阻塞当前线程,程序会立即退出,线程池里的任务来不及执行,程序就已经退出了。awaitTerminationSeconds 代表程序最多等待任务执行的时间,在这之后哪怕任务没执行完,也会退出程序。
总结,Spring 环境下的线程池优雅关闭,需要设置 waitForTasksToCompleteOnShutdown = true,再设置一个充分的 awaitTerminationSeconds 让线程池有足够多的时间去执行剩下的任务。万一还是有任务超时了,就得通过改造把这些任务数据保存下来,下次继续执行。
任务依赖其它资源的问题
在 Spring 环境下,简单的异步任务通过设置一个足够长的 awaitTerminationSeconds 就可解决问题。一旦异步任务的执行依赖其它资源,但是 Spring 恰好又把这些资源释放了,任务的执行还是会失败。
举个例子,异步任务需要往 Redis 里面写数据,Spring 在收到程序退出的信号时,就会关闭上下文环境,销毁 IOC 容器释放资源。结果就是,线程池里的任务还在跑,但是 Redis 连接已经关闭了,可想而知,任务一定会跑失败。这也不是我们想要的结果。
下面这段示例代码,任务执行耗时1秒,最后会往 Redis 写数据,一次性注册5个任务,2秒后kill掉进程,给线程池预留3秒的执行时间,理论上5个任务都会执行完。
@Component
@RequiredArgsConstructor
public class TaskRunner implements CommandLineRunner {private final Log log = LogFactory.getLog("TaskRunner");private final ThreadPoolTaskExecutor taskExecutor;private final RedisClient redisClient;@Overridepublic void run(String... args) throws Exception {for (int i = 0; i < 5; i++) {final int index = i + 1;taskExecutor.submit(() -> {try {Threads.sleep(1000L);redisClient.write("key", String.valueOf(index));} catch (Exception e) {e.printStackTrace();}});}}
}
运行的结果是,5个任务都执行了,但是后面3个都失败了,因为 Redis 连接已经被关闭。
要解决这个问题,得从 Spring 销毁 Bean 的顺序上入手,因为线程池依赖 RedisClient,所以 RedisClient 必须在线程池销毁之后再销毁,好在 Spring 提供了相应的注解,只需加上@DependsOn
即可。
@Configuration
public class ThreadPoolConfig {@Bean("taskExecutor")@DependsOn("redisClient")// 确保在redisClient之前销毁public ThreadPoolTaskExecutor threadPoolTaskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setThreadNamePrefix("TASK-");executor.setCorePoolSize(1);executor.setMaxPoolSize(1);executor.setQueueCapacity(100);executor.setKeepAliveSeconds(60);executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());executor.setWaitForTasksToCompleteOnShutdown(true);executor.setAwaitTerminationSeconds(3);return executor;}
}
再跑一遍,5个任务都执行成功了。
最后还有一个问题,异步任务很复杂,具体我也不知道依赖哪些外部资源,没办法针对性的配置,怎么办?
这种场景下,我建议直接监听 ContextClosedEvent。因为 ContextClosedEvent 是早于 Bean 销毁前发出的,在监听器里面等到线程池全部销毁完毕,再让 Spring 销毁其它 bean。也就是说,线程池的销毁必须先于其它所有 bean 之前,简单粗暴。
下面是一个简单示例:
@Component
public class ApplicationCloseListener implements ApplicationListener<ContextClosedEvent>, ApplicationContextAware {ApplicationContext applicationContext;@Overridepublic void onApplicationEvent(ContextClosedEvent event) {// 取出所有ThreadPoolTaskExecutor,等待销毁applicationContext.getBeansOfType(ThreadPoolTaskExecutor.class).values().forEach(ExecutorConfigurationSupport::destroy);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}
}
尾巴
线程池的优雅关闭,关键在于给它一个充分的超时时间,确保剩余任务在这个时间内可以执行完毕。万一还有没执行的任务,也可以先把数据保存下来,后续重新执行。在 Spring 环境下针对线程池做了二次封装,通过配置即可实现优雅关闭,但是需要注意异步任务依赖其它 bean 的问题,依赖的 bean 可能早于线程池被销毁,任务执行就会报错,可以通过配置和监听 ContextClosedEvent 两种方式延迟销毁依赖的 bean。
相关文章:
Spring线程池优雅关闭
前言 线程池大家一定不陌生,常被用来异步执行一些耗时的任务。但是线程池如何优雅的关闭,却少有人关注。 当 JVM 进程关闭时,你提交到线程池的任务会被如何处理?如何保证任务不丢? ThreadPoolExecutor Java 对线程…...
Spring为什么要用三级缓存解决循环依赖?
1.什么是循环依赖 本文为了方便说明,先设置两个业务层对象,命名为AService和BService。其中Spring是如何把一个Bean对象创建出来的,其生命周期如下: 构造方法–> 不同对象 --> 注入依赖 -->初始化前 --> 初始化后–&…...
【苏德矿高等数学】第4讲:数列极限定义-1
2. 数列极限 数列极限是整个微积分的核心。它的思想贯穿整个微积分之中。 数列极限是最基本的、最核心的、最重要的、最难的。 2.1 数列 【定义】无限排列的一列数 a 1 , a 2 , ⋯ , a n , ⋯ a_1,a_2,\cdots,a_n,\cdots a1,a2,⋯,an,⋯就称为数列,记作 { …...
Go语言的 的并发编程(Concurrency)核心知识
Go语言的并发编程(Concurrency)核心知识 在现代软件开发中,尤其是处理高并发任务时,优秀的并发编程能力显得尤为重要。Go语言(或称Golang)是为并发编程而生的一种编程语言,它通过简洁的语法和强…...
Go语言中的逃逸分析:深入浅出
Go语言中的逃逸分析:深入浅出 在Go语言中,逃逸分析(Escape Analysis)是一个非常重要且强大的编译器优化技术。它帮助编译器决定一个变量是在栈上分配还是在堆上分配,从而影响程序的性能和内存管理。本文将深入探讨Go语…...
flux中的缓存
1. cache,onBackpressureBuffer。都是缓存。cache可以将hot流的数据缓存起来。onBackpressureBuffer也是缓存,但是当下游消费者的处理速度比上游生产者慢时,上游生产的数据会被暂时存储在缓冲区中,防止丢失。 2. Flux.range 默认…...
Vue3中使用 Vue Flow 流程图方法
效果图: 最近项目开发时有一个流程图的功能,需要做流程节点的展示,就搜到了 Vue Flow 这个插件,这个插件总得来说还可以,简单已使用,下边就总结一下使用的方法: Vue Flow官网:https…...
[Effective C++]条款42 typename
本文初发于 “天目中云的小站”,同步转载于此。 条款42 : 了解typename的双重意义 本条款中我们将了解typename的两种使用场景, 对typename的内涵及使用加深认知. template声明式 在template的声明中, template<class T>和template<typename T>都是被允…...
MySQL 8 主从同步配置(Master-Slave Replication)
📋 MySQL 8 主从同步配置(Master-Slave Replication) 🔧 目标: 配置 MySQL 8 主从同步,实现 主库(Master) 处理写操作,从库(Slave) 处理读操作,达到 读写分离 和 高可用性 的目的。 🔑 核心步骤: 配置 主库(Master)配置 从库(Slave)启动主从复制验证主从…...
STM32第十一课:STM32-基于标准库的42步进电机的简单IO控制(附电机教程,看到即赚到)
一:步进电机简介 步进电机又称为脉冲电机,简而言之,就是一步一步前进的电机。基于最基本的电磁铁原理,它是一种可以自由回转的电磁铁,其动作原理是依靠气隙磁导的变化来产生电磁转矩,步进电机的角位移量与输入的脉冲个数严格成正…...
模拟(算法-6)
模拟简介 模拟就是根据题目要求,比着葫芦画瓢,即直接按照题目要求写就行了 考察的是我们的编码能力 步骤: 演草纸上画图模拟(重要) 代码编写 虽然很多时候此类题比较简单,但是也有例外,如本文第…...
Clickhouse集群部署(3分片1副本)
Clickhouse集群部署 3台Linux服务器,搭建Clickhouse集群3分片1副本模式 1、安装Java、Clickhouse、Zookeeper dpkg -i clickhouse-client_23.2.6.34_amd64.deb dpkg -i clickhouse-common-static_23.2.6.34_amd64.deb dpkg -i clickhouse-server_23.2.6.34_amd64…...
MySQL(六)MySQL 案例
1. MySQL 案例 1.1. 设计数据库 1、首先根据相关业务需求(主要参考输出输入条件)规划出表的基本结构 2、根据业务规则进行状态字段设计 3、预估相关表的数据量进行容量规划 4、确定主键 5、根据对相关处理语句的分析对数据结构进行相应的变更。 设计表的时…...
【网络协议栈】TCP/IP协议栈中重要协议和技术(DNS、ICMP、NAT、代理服务器、以及内网穿透)
每日激励:“请给自己一个鼓励说:Jack我很棒!—Jack” 绪论: 本章是TCP/IP网络协议层的完结篇,本章将主要去补充一些重要的协议和了解一些网络中常见的名词,具体如:DNS、ICMP、NAT、代理服务器…...
NLP中特征提取方法的总结
1. Bag of Words (BOW) 描述:将文本表示为一个词汇表中的词频向量,忽略词的顺序。 优点:实现简单,广泛应用。 缺点:不考虑词序和上下文信息,向量空间维度可能非常大。 应用:文本分类、情感分…...
《HarmonyOS第一课》焕新升级,赋能开发者快速掌握鸿蒙应用开发
随着HarmonyOS NEXT发布,鸿蒙生态日益壮大,广大开发者对于系统化学习平台和课程的需求愈发强烈。近日,华为精心打造的《HarmonyOS第一课》全新上线,集“学、练、考”于一体,凭借多维融合的教学模式与系统课程设置&…...
JMeter + Grafana +InfluxDB性能监控 (二)
您可以通过JMeter、Grafana 和 InfluxDB来搭建一个炫酷的基于JMeter测试数据的性能测试监控平台。 下面,笔者详细介绍具体的搭建过程。 安装并配置InfluxDB 您可以从清华大学开源软件镜像站等获得InfluxDB的RPM包,这里笔者下载的是influxdb-1.8.0.x86_…...
【微服务】3、配置管理
微服务配置管理 已掌握的微服务组件及配置管理问题引出 已掌握注册中心、Openfan、远程调用、负载均衡、网关等组件,具备微服务开发能力,但仍存在其他问题待解决。微服务和网关存在大量配置文件,其中包含很多重复配置,如数据库、日…...
数据结构(顺序表)
文章目录 数据结构概述什么是数据结构数据结构的类型常见的数据结构 线性表概念举例 顺序表基本概念基本操作 完整代码顺序表优缺点总结 数据结构概述 什么是数据结构 数据结构:数据结构就是计算机存储,组织,管理数据的方式方法 数据结构的…...
ARM架构服务器安装部署KVM虚拟化环境
一、查看内核是否支持KVM虚拟化 针对ARM架构服务器,若/dev/kvm 和 /sys/module/kvm任意一个不存在,都说明内核不支持KVM虚拟化 [rootlocalhost ~]# ls -l /dev/kvm crw-rw---- 1 root kvm 10, 232 May 6 09:18 /dev/kvm[rootlocalhost ~]# ls /sys/mo…...
Azkaban其二,具体使用以及告警设置
目录 Azkaban的使用 1、使用Flow1.0(比较老旧) 2、Flow2.0的用法 1、小试牛刀 2、YAML格式的数据 3、多任务依赖 4、内嵌流(嵌套流)案例 5、动态传参 3、Azkaban的报警机制 1)邮箱通知 2)电话报警机制 4、关…...
不只是mini-react第一节:实现最简单mini-react
项目总结构: ├─ 📁core │ ├─ 📄React.js │ └─ 📄ReactDom.js ├─ 📁node_modules ├─ 📁tests │ └─ 📄createElement.spec.js ├─ 📄App.js ├─ 📄in…...
MySQL数据库备份与恢复策略
数据是企业和应用的核心资产,可靠的备份和恢复策略是确保数据安全性和业务连续性的关键。在本篇文章中,我们将详细介绍 MySQL 数据库的备份和恢复方法,包括逻辑备份、物理备份、自动化备份,以及常见问题的处理方法。 一、逻辑备份 逻辑备份是通过导出数据库的结构和数据生…...
SpringBoot下载文件的几种方式
小文件:直接将文件一次性读取到内存中,文件大可能会导致OOM GetMapping("/download1")public void download1(HttpServletResponse response) throws IOException {// 指定要下载的文件File file new File("C:\\Users\\syd\\Desktop\\do…...
探索现代 Web 开发中的流行技术:深入学习 Vite 的使用
在前端开发的世界中,构建工具扮演着越来越重要的角色。从 Webpack 到 Parcel,再到 Rollup,每个工具都有自己的独特定位和目标。而今天,我们要讨论的是一款近年来迅速崛起并受到广泛欢迎的构建工具— Vite。 本文将从基本原理到实…...
React虚拟DOM:理解和应用
写在前面 在现代前端开发中,React 是一个非常流行的 JavaScript 库,用于构建用户界面。它引入了一个名为“虚拟 DOM”(Virtual DOM)的概念,这个概念对于 React 的高效性能和易用性至关重要。本文将深入探讨 React Vir…...
C++和SFML游戏入门
让我们深入探讨一下如何使用C和SFML(Simple and Fast Multimedia Library)来创建一个更为详细的游戏代码示例。我们将构建一个简单的2D游戏,其中包含一个可移动的角色、基本的碰撞检测以及简单的得分系统。 项目结构 首先,我们定…...
Django 表单
Django 表单 Django 表单是 Web 开发中一个重要的组成部分,它允许用户与网站进行交互。在 Django 框架中,表单用于收集和验证用户输入的数据。Django 提供了一个强大的表单处理系统,使得开发者能够轻松地创建表单,处理表单数据,并进行验证。 1. Django 表单基础 Django…...
Ubuntu 安装英伟达显卡驱动问题记录
建议 无论什么版本的系统,都建议从官网下载驱动,一般下载下来后是一个 run 开头的可执行程序,直接在命令行执行就可以。 之所以这么建议,是因为使用 包管理工具安装的驱动一般都是社区版本,社区版本的问题就是没有签名,需要在 BIOS 内关闭 security boot 才可以加载对应…...
机器视觉面试题PDF
以下是一些机器视觉面试题PDF资源的推荐: GitHub仓库• 《百面计算机视觉》面试题整理:这个GitHub仓库整理了计算机视觉算法岗的面试题,包括深度学习基础、机器学习高频面试题、传统CV(Opencv面试题)、目标检测、图像分类、图像分割、Transfo…...
【网络协议】IPv4 地址分配 - 第一部分
文章目录 十进制与二进制网络如何被寻址地址类型网络地址广播地址主机地址 如何确定网络和主机部分的位数?网络中的主机数量与前缀号的关系计算每个前缀的主机数量公式 子网掩码二进制与操作(Binary ANDing)与操作(AND Operation&…...
掌握 Dockerfile:格式、解析器指令、环境变量替换
Docker 是一个开源平台,旨在自动化应用程序的构建、交付和运行。通过 Dockerfile,您可以定义镜像的构建过程。Dockerfile 是由一系列指令组成的文件,Docker 根据这些指令构建镜像。本文将介绍常用的 Dockerfile 指令、格式、解析器指令以及环…...
AWS Glue基础知识
AWS Glue 是一项完全托管的 ETL(提取、转换、加载)服务,与考试相关,尤其是在数据集成、处理和分析方面。 1.数据集成和 ETL(提取、转换、加载) AWS Glue 主要用于构建 ETL 管道以准备数据以进行分析。作为…...
单片机-LED实验
1、51工程模版 #include "reg52.h" void main(){ while(1){ } } 2、LED灯亮 #include "reg52.h" sbit LED1P2^0; void main(){ while(1){ LED10; } } 3、LED闪烁 #include "reg52.h" sbit LED1P2^0; //P2大…...
使用Python实现健康跟踪应用:打造智能健康管理助手
随着人们对健康的关注日益增强,智能健康管理逐渐成为热门趋势。通过持续跟踪身体状况、饮食、运动和睡眠等方面的数据,我们可以更好地了解自己的健康状态,并采取相应的措施来保持身体健康。Python,作为一种简洁且功能强大的编程语言,非常适合用来开发健康跟踪应用。本文将…...
结构型模式6.享元模式
结构型模式 适配器模式(Adapter Pattern)桥接模式(Bridge Pattern)组合模式(Composite Pattern)装饰器模式(Decorator Pattern)外观模式(Facade Pattern)享元…...
AWS S3文件存储工具类
pom依赖 <!--aws-s3--> <dependency><groupId>com.amazonaws</groupId><artifactId>aws-java-sdk-s3</artifactId><version>1.12.95</version></dependency>S3Utils import cn.hutool.core.util.ZipUtil; import com.a…...
MacDriver 项目推荐
MacDriver 项目推荐 macdriver Native Mac APIs for Go. Soon to be renamed DarwinKit! 项目地址: https://gitcode.com/gh_mirrors/ma/macdriver 1. 项目基础介绍和主要编程语言 MacDriver 是一个开源项目,旨在为 Go 语言提供原生的 macOS API 支持。该项…...
笔记:一次mysql主从复制延迟高的处理尝试
背景 mysql 5.7 主从复制 主库进行了一次灌数,导致多个大事务产生,主从延迟下不去,经确认该表最终truncate,并且该表仅有insert和select操作,故对该表的事务进行跳过,直到同步至truncate 跳过事务需谨慎&…...
《Vue3实战教程》40:Vue3安全
如果您有疑问,请观看视频教程《Vue3实战教程》 安全 报告漏洞 当一个漏洞被上报时,它会立刻成为我们最关心的问题,会有全职的贡献者暂时搁置其他所有任务来解决这个问题。如需报告漏洞,请发送电子邮件至 securityvuejs.org。…...
Linux 基础 6.进程
文章目录 6.1 进程和程序1. **程序 (Program)**2. **进程 (Process)**3. **程序与进程的区别**4. **进程的创建与执行**5. **总结** 6.2 进程号和父进程号1. **进程号 (PID)**2. **进程号的分配**3. **父进程号 (PPID)**4. **进程树结构**5. **进程号的限制与调整**6. **总结**…...
LeetCode:700.二叉搜索树中的搜索
跟着carl学算法,本系列博客仅做个人记录,建议大家都去看carl本人的博客,写的真的很好的! 代码随想录 LeetCode:700.二叉搜索树中的搜索 给定二叉搜索树(BST)的根节点 root 和一个整数值 val。 你…...
web实操9——session
概念 数据保存在服务器HttpSession对象里。 session也是域对象,有setAttribute和getAttribute方法 快速入门 代码 获取session和塞入数据: 获取session获取数据: 请求存储: 请求获取: 数据正常打印:…...
有哪几种方法可以使html脱离文档流?
position: absolute:会使元素脱离文档流,并且相对于最近的已定位的祖先元素进行定位。如果没有已定位的祖先元素,则相对于 <html> 元素定位。 元素脱离文档流,不再占据空间。 不会影响后续元素的位置和布局。 可以通过 top、right、…...
戴尔/Dell 电脑按什么快捷键可以进入 Bios 设置界面?
BIOS(基本输入输出系统)是计算机硬件与操作系统之间的桥梁,它负责初始化和测试系统硬件组件,并加载启动操作系统。在某些情况下,如调整启动顺序、更改系统时间或日期、修改硬件配置等,您可能需要进入BIOS进…...
Fast R-CNN模型详解及分析
模型背景 在目标检测领域的发展历程中,R-CNN系列模型标志着一个重要转折点。然而,在Fast R-CNN问世之前,这一领域的研究仍面临一些显著挑战: 计算效率低下 :早期模型如R-CNN和SPPNet虽然在准确性方面取得进展,但在计算效率上仍有待提高。特别是R-CNN需要多次运行CNN并单…...
android studio 写一个小计时器(版本二)
as版本:23.3.1patch2 例程:timer 在前一个版本的基本上改的,增加了继续的功能,实现方法稍微不同。 动画演示: activity_main.xml <?xml version"1.0" encoding"utf-8"?> <androidx…...
STM32完全学习——使用定时器1精确延时
一、定时器的相关配置 首先一定要是递减定时器,递增的不太行,控制的不够准确,其次在大于10微秒的延时是非常准确的,小于的话,就没有那没准,但是凑合能用。误差都在一个微秒以内。使用高级定时器也就是时钟…...
如何在 Ubuntu 22.04 上安装 Cassandra NoSQL 数据库教程
简介 本教程将向你介绍如何在 Ubuntu 22.04 上安装 Cassandra NoSQL 数据库。 Apache Cassandra 是一个分布式的 NoSQL 数据库,旨在处理跨多个普通服务器的大量数据,并提供高可用性,没有单点故障。Apache Cassandra 是一个高度可扩展的分布…...
【MATLAB第112期】基于MATLAB的SHAP可解释神经网络回归模型(敏感性分析方法)
【MATLAB第112期】基于MATLAB的SHAP可解释神经网络回归模型(敏感性分析方法) 引言 该文章实现了一个可解释的神经网络回归模型,使用BP神经网络(BPNN)来预测特征输出。该模型利用七个变量参数作为输入特征进行训练。为…...