SpringBoot异步处理@Async深度解析:从基础到高阶实战
一、异步编程基础概念
1.1 同步 vs 异步
特性 | 同步 | 异步 |
---|---|---|
执行方式 | 顺序执行,阻塞调用 | 非阻塞,调用后立即返回 |
线程使用 | 单线程完成所有任务 | 多线程并行处理 |
响应性 | 较差,需等待前任务完成 | 较好,可立即响应新请求 |
复杂度 | 简单直观 | 较复杂,需处理线程安全 |
适用场景 | 简单流程,短时间任务 | IO密集型,长时间任务 |
通俗理解:同步就像在银行柜台排队办理业务,必须等前面的人办完才能轮到你;异步则像取号后可以坐着玩手机,等叫号时再去办理。
1.2 为什么要使用异步
- 提高吞吐量:服务器能同时处理更多请求
- 增强用户体验:避免用户长时间等待
- 资源优化:合理利用系统资源,避免阻塞主线程
- 解耦:将耗时操作与主流程分离
1.3 Java中的异步编程方式
// 1. 传统Thread方式
new Thread(() -> {// 异步任务
}).start();// 2. Future模式
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> {Thread.sleep(1000);return "Result";
});// 3. CompletableFuture (Java8+)
CompletableFuture.supplyAsync(() -> {// 异步任务return "Result";
}).thenAccept(result -> {// 处理结果
});// 4. Spring @Async (本文重点)
@Async
public void asyncMethod() {// 异步方法体
}
二、@Async基础使用
2.1 启用@Async支持
步骤1:在Spring Boot主类或配置类上添加@EnableAsync
@SpringBootApplication
@EnableAsync // 启用异步支持
public class AsyncApplication {public static void main(String[] args) {SpringApplication.run(AsyncApplication.class, args);}
}
步骤2:创建异步服务类
@Service
public class EmailService {// 无返回值异步方法@Asyncpublic void sendEmail(String to, String content) {// 模拟邮件发送耗时try {Thread.sleep(3000);System.out.println("邮件已发送至: " + to + ", 内容: " + content);} catch (InterruptedException e) {e.printStackTrace();}}// 有返回值异步方法@Asyncpublic Future<String> sendEmailWithResult(String to, String content) {try {Thread.sleep(3000);String result = "邮件已发送至: " + to;return new AsyncResult<>(result);} catch (InterruptedException e) {return new AsyncResult<>("发送失败");}}
}
2.2 调用异步方法
@RestController
@RequestMapping("/api/email")
public class EmailController {@Autowiredprivate EmailService emailService;@GetMapping("/send")public String sendEmail() {long start = System.currentTimeMillis();// 调用异步方法emailService.sendEmail("user@example.com", "您的订单已创建");long elapsed = System.currentTimeMillis() - start;return "请求已处理,耗时: " + elapsed + "ms"; // 立即返回,不会等待邮件发送完成}@GetMapping("/send-with-result")public String sendEmailWithResult() throws Exception {Future<String> future = emailService.sendEmailWithResult("user@example.com", "订单详情");// 可以在这里做其他事情// 当需要结果时(阻塞等待)String result = future.get();return "处理结果: " + result;}
}
2.3 @Async方法限制
- 必须public修饰:因为基于Spring AOP实现
- 同类调用无效:
this.asyncMethod()
不会异步执行 - 返回值限制:
- void
- Future及其子类(如AsyncResult)
- CompletableFuture (Spring 4.2+)
- ListenableFuture (Spring 4.2+)
三、线程池配置详解
3.1 默认线程池问题
Spring默认使用SimpleAsyncTaskExecutor
,它不重用线程,每次调用都创建新线程,生产环境不推荐使用。
3.2 自定义线程池配置
方式1:配置类方式(推荐)
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 核心线程数:线程池创建时初始化的线程数executor.setCorePoolSize(5);// 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程executor.setMaxPoolSize(10);// 缓冲队列:用来缓冲执行任务的队列executor.setQueueCapacity(50);// 线程名前缀executor.setThreadNamePrefix("Async-Executor-");// 线程池关闭时等待所有任务完成executor.setWaitForTasksToCompleteOnShutdown(true);// 等待时间executor.setAwaitTerminationSeconds(60);// 拒绝策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new CustomAsyncExceptionHandler();}
}// 自定义异常处理器
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {@Overridepublic void handleUncaughtException(Throwable ex, Method method, Object... params) {System.err.println("异步任务异常 - 方法: " + method.getName());System.err.println("异常信息: " + ex.getMessage());// 这里可以添加自定义处理逻辑,如记录日志、发送告警等}
}
方式2:使用@Bean定义多个线程池
@Configuration
public class TaskExecutorConfig {@Bean(name = "emailExecutor")public Executor emailTaskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(3);executor.setMaxPoolSize(5);executor.setQueueCapacity(30);executor.setThreadNamePrefix("Email-Executor-");executor.initialize();return executor;}@Bean(name = "reportExecutor")public Executor reportTaskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(2);executor.setMaxPoolSize(4);executor.setQueueCapacity(20);executor.setThreadNamePrefix("Report-Executor-");executor.initialize();return executor;}
}
使用指定线程池:
@Async("emailExecutor")
public void sendEmail(String to) { /*...*/ }@Async("reportExecutor")
public void generateReport() { /*...*/ }
3.3 线程池参数详解
参数名 | 说明 | 推荐设置建议 |
---|---|---|
corePoolSize | 核心线程数,即使空闲也不会被回收 | CPU密集型:CPU核数+1 IO密集型:2*CPU核数 |
maxPoolSize | 最大线程数,当队列满时才会创建新线程直到此值 | 建议为核心线程数的2-3倍 |
queueCapacity | 任务队列容量,超过核心线程数的任务会进入队列 | 根据业务量调整,太大可能导致OOM |
keepAliveSeconds | 非核心线程空闲存活时间(秒) | 60-300秒 |
threadNamePrefix | 线程名前缀,便于监控和日志追踪 | 建议按业务命名,如"Order-Async-" |
allowCoreThreadTimeOut | 是否允许核心线程超时退出 | 默认false,长时间空闲应用可设为true |
waitForTasksToCompleteOnShutdown | 应用关闭时是否等待异步任务完成 | 生产环境建议true |
awaitTerminationSeconds | 等待任务完成的超时时间 | 根据业务最长执行时间设置 |
rejectedExecutionHandler | 拒绝策略,当线程池和队列都满时的处理方式 | 根据业务需求选择 |
拒绝策略选项:
AbortPolicy
:默认,直接抛出RejectedExecutionExceptionCallerRunsPolicy
:由调用者线程执行该任务DiscardPolicy
:直接丢弃任务DiscardOldestPolicy
:丢弃队列中最老的任务并重试
3.4 线程池监控
@Service
public class ThreadPoolMonitor {@Autowiredprivate ThreadPoolTaskExecutor emailExecutor;@Scheduled(fixedRate = 5000) // 每5秒监控一次public void monitor() {System.out.println("=== 线程池监控 ===");System.out.println("当前线程数: " + emailExecutor.getPoolSize());System.out.println("活跃线程数: " + emailExecutor.getActiveCount());System.out.println("完成任务数: " + emailExecutor.getCompletedTaskCount());System.out.println("队列剩余容量: " + emailExecutor.getThreadPoolExecutor().getQueue().remainingCapacity());}
}
四、@Async高级特性
4.1 返回值处理
1. Future模式
@Async
public Future<String> processData(String input) {// 模拟处理耗时try {Thread.sleep(2000);return new AsyncResult<>("处理完成: " + input.toUpperCase());} catch (InterruptedException e) {Thread.currentThread().interrupt();return new AsyncResult<>("处理中断");}
}// 调用方
Future<String> future = service.processData("hello");
// 可以做其他事情...
String result = future.get(3, TimeUnit.SECONDS); // 带超时的等待
2. CompletableFuture (Java8+)
@Async
public CompletableFuture<String> fetchData(String param) {return CompletableFuture.supplyAsync(() -> {try {Thread.sleep(1000);return "Data for " + param;} catch (InterruptedException e) {throw new RuntimeException(e);}});
}// 链式调用
service.fetchData("user123").thenApply(String::toUpperCase).thenAccept(System.out::println).exceptionally(ex -> {System.err.println("Error: " + ex.getMessage());return null;});
4.2 基于条件的异步执行
1. 使用Spring Expression Language (SpEL)
@Async("#{systemProperties['async.enabled'] ? 'emailExecutor' : 'syncExecutor'}")
public void conditionalAsync() {// 根据系统属性决定使用哪个执行器
}
2. 基于配置的开关
@Async
@ConditionalOnProperty(name = "app.async.enabled", havingValue = "true")
public void configBasedAsync() {// 当app.async.enabled=true时才异步执行
}
4.3 事务处理
异步方法与事务的特殊关系:
- 事务边界:
@Async
方法会在新线程中执行,与原方法不在同一事务中 - 传播行为:需要在异步方法上单独声明
@Transactional
@Async
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void asyncWithTransaction() {// 这个方法会在新事务中执行userRepository.save(new User("AsyncUser"));// 如果发生异常,只会回滚当前方法内的操作
}
4.4 组合异步操作
场景:需要等待多个异步任务全部完成
@Async
public CompletableFuture<String> fetchUserInfo(String userId) {// 模拟获取用户信息return CompletableFuture.completedFuture("UserInfo-" + userId);
}@Async
public CompletableFuture<String> fetchOrderInfo(String userId) {// 模拟获取订单信息return CompletableFuture.completedFuture("OrderInfo-" + userId);
}// 组合多个异步任务
public CompletableFuture<Void> combineAsyncTasks(String userId) {return CompletableFuture.allOf(fetchUserInfo(userId),fetchOrderInfo(userId)).thenRun(() -> {// 所有任务完成后的处理System.out.println("所有异步任务已完成");});
}
五、异常处理机制
5.1 异常处理方式对比
处理方式 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
AsyncUncaughtExceptionHandler | 处理void返回类型的异步方法异常 | 集中处理,统一管理 | 无法获取方法返回值 |
Future.get() | 处理有返回值的异步方法异常 | 可以获取具体异常信息 | 需要手动调用get() |
CompletableFuture.exceptionally | Java8+的优雅异常处理方式 | 链式调用,代码简洁 | 仅适用于CompletableFuture |
5.2 实践示例
1. 全局异常处理器
public class GlobalAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {private static final Logger logger = LoggerFactory.getLogger(GlobalAsyncExceptionHandler.class);@Overridepublic void handleUncaughtException(Throwable ex, Method method, Object... params) {logger.error("异步任务异常 - 方法: {}, 参数: {}", method.getName(), Arrays.toString(params), ex);// 可以根据异常类型进行不同处理if (ex instanceof BusinessException) {// 业务异常处理sendAlert("业务异常警报: " + ex.getMessage());} else if (ex instanceof TimeoutException) {// 超时处理retryTask(method, params);}}private void sendAlert(String message) { /*...*/ }private void retryTask(Method method, Object... params) { /*...*/ }
}
2. Future方式的异常处理
@Async
public Future<String> asyncTaskWithException() {try {// 业务逻辑if (someCondition) {throw new BusinessException("业务异常");}return new AsyncResult<>("成功");} catch (BusinessException e) {return new AsyncResult<>("失败: " + e.getMessage());}
}// 调用方处理
Future<String> future = service.asyncTaskWithException();
try {String result = future.get();if (result.startsWith("失败")) {// 处理失败情况}
} catch (ExecutionException e) {// 处理执行时异常
}
3. CompletableFuture的异常处理
@Async
public CompletableFuture<String> asyncProcess(String input) {return CompletableFuture.supplyAsync(() -> {if (input == null) {throw new IllegalArgumentException("输入不能为空");}return "处理结果: " + input.toUpperCase();});
}// 调用方处理
service.asyncProcess(null).exceptionally(ex -> {System.err.println("发生异常: " + ex.getMessage());return "默认返回值";}).thenAccept(result -> {System.out.println("最终结果: " + result);});
六、性能优化与最佳实践
6.1 性能优化建议
-
线程池参数调优
- 根据业务类型调整线程池大小
- 监控线程池状态动态调整参数
- 使用有界队列防止OOM
-
避免长时间阻塞
- 异步方法内避免同步阻塞操作
- 使用带超时的阻塞调用
-
资源清理
- 确保异步方法正确释放资源
- 使用try-with-resources管理资源
-
上下文传递
- 注意ThreadLocal变量在异步线程中的传递问题
- 使用
TaskDecorator
传递上下文
executor.setTaskDecorator(new ContextCopyingDecorator());public class ContextCopyingDecorator implements TaskDecorator {@Overridepublic Runnable decorate(Runnable runnable) {// 获取当前线程的上下文RequestAttributes context = RequestContextHolder.currentRequestAttributes();return () -> {try {// 在新线程中设置上下文RequestContextHolder.setRequestAttributes(context);runnable.run();} finally {RequestContextHolder.resetRequestAttributes();}};}
}
6.2 最佳实践清单
-
命名规范
- 异步方法名以
Async
后缀标识,如sendEmailAsync
- 线程池按业务命名,如
orderTaskExecutor
- 异步方法名以
-
日志记录
- 记录异步任务开始/结束时间
- 为异步线程设置可追踪的上下文ID
@Async
public void asyncWithLogging() {String traceId = UUID.randomUUID().toString();MDC.put("traceId", traceId);try {log.info("异步任务开始");// 业务逻辑log.info("异步任务完成");} finally {MDC.clear();}
}
-
防御性编程
- 检查异步方法参数有效性
- 添加合理的超时控制
-
资源限制
- 限制并发异步任务数量
- 对重要任务设置优先级
-
监控告警
- 监控线程池关键指标
- 设置异常告警阈值
七、与其他技术的整合
7.1 与Spring Retry整合
实现异步任务失败重试:
@Async
@Retryable(value = {RemoteAccessException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2))
public CompletableFuture<String> callExternalService() {// 调用可能失败的外部服务return CompletableFuture.completedFuture(externalService.call());
}// 重试全部失败后的处理
@Recover
public CompletableFuture<String> recover(RemoteAccessException e) {return CompletableFuture.completedFuture("默认返回值");
}
7.2 与Spring Cache整合
异步缓存更新:
@Async
@CacheEvict(value = "users", key = "#userId")
public void evictUserCacheAsync(String userId) {// 异步清理缓存
}@Async
@CachePut(value = "users", key = "#user.id")
public CompletableFuture<User> updateUserAsync(User user) {// 异步更新用户并更新缓存return CompletableFuture.completedFuture(userRepository.save(user));
}
7.3 与WebFlux响应式编程对比
特性 | @Async | WebFlux |
---|---|---|
编程模型 | 命令式 | 响应式 |
线程模型 | 线程池-based | 事件循环 |
资源消耗 | 较高(每个请求一个线程) | 较低(少量线程处理所有请求) |
学习曲线 | 较低 | 较高 |
适用场景 | 传统Servlet应用 | 高并发IO密集型应用 |
背压支持 | 不支持 | 支持 |
集成复杂度 | 简单 | 中等 |
八、常见问题与解决方案
8.1 问题排查表
问题现象 | 可能原因 | 解决方案 |
---|---|---|
@Async方法不异步执行 | 同类调用 | 确保通过Spring代理调用,使用@Autowired注入自身 |
未加@EnableAsync | 在主配置类添加@EnableAsync | |
异步方法抛出异常不显示 | 未正确处理AsyncUncaughtException | 实现AsyncUncaughtExceptionHandler |
线程池不生效 | 未正确命名或注入 | 确保@Async(“executorName”)与@Bean名称一致 |
应用关闭时任务丢失 | 未配置优雅关闭 | 设置setWaitForTasksToCompleteOnShutdown(true)和awaitTerminationSeconds |
性能未提升反而下降 | 线程池配置不合理 | 调整核心/最大线程数和队列容量 |
ThreadLocal值丢失 | 线程切换导致上下文丢失 | 使用TaskDecorator传递上下文 |
8.2 实战问题案例
案例1:数据库连接泄漏
@Async
public void processData() {// 错误示范:未关闭ConnectionConnection conn = dataSource.getConnection();// 使用conn...
}
解决方案:
@Async
public void processData() {try (Connection conn = dataSource.getConnection()) {// 使用conn...} catch (SQLException e) {// 异常处理}
}
案例2:订单超时未支付取消
@Async
@Scheduled(fixedDelay = 60000) // 每分钟检查一次
public void cancelUnpaidOrders() {List<Order> unpaidOrders = orderRepository.findByStatusAndCreateTimeBefore(OrderStatus.UNPAID, LocalDateTime.now().minusMinutes(30));unpaidOrders.forEach(order -> {order.setStatus(OrderStatus.CANCELLED);orderRepository.save(order);notificationService.sendCancelNotice(order);});
}
九、总结
9.1 核心要点总结
-
基础使用:
@EnableAsync
启用支持@Async
标注异步方法- 避免同类调用
-
线程池配置:
- 生产环境必须自定义线程池
- 合理设置核心参数
- 监控线程池状态
-
异常处理:
- 区分返回值类型选择处理方式
- 实现全局异常处理器
- 日志记录完整上下文
-
高级特性:
- 组合多个异步操作
- 事务边界处理
- 条件异步执行
-
最佳实践:
- 命名规范
- 防御性编程
- 资源清理
- 上下文传递
9.2 完整示例代码
订单处理服务示例:
@Service
public class OrderProcessingService {private static final Logger logger = LoggerFactory.getLogger(OrderProcessingService.class);@Autowiredprivate OrderRepository orderRepository;@Autowiredprivate PaymentService paymentService;@Autowiredprivate NotificationService notificationService;@Async("orderTaskExecutor")@Transactional(propagation = Propagation.REQUIRES_NEW)public CompletableFuture<OrderResult> processOrderAsync(Order order) {logger.info("开始异步处理订单: {}", order.getId());long startTime = System.currentTimeMillis();try {// 1. 保存订单Order savedOrder = orderRepository.save(order);// 2. 处理支付PaymentResult paymentResult = paymentService.processPayment(savedOrder);if (!paymentResult.isSuccess()) {throw new PaymentException("支付处理失败: " + paymentResult.getErrorMessage());}// 3. 更新订单状态savedOrder.setStatus(OrderStatus.PAID);orderRepository.save(savedOrder);// 4. 发送通知notificationService.sendOrderConfirmation(savedOrder);long elapsed = System.currentTimeMillis() - startTime;logger.info("订单处理完成: {}, 耗时: {}ms", savedOrder.getId(), elapsed);return CompletableFuture.completedFuture(new OrderResult(true, "订单处理成功", savedOrder));} catch (PaymentException e) {logger.error("订单支付异常: {}", order.getId(), e);return CompletableFuture.completedFuture(new OrderResult(false, e.getMessage(), order));} catch (Exception e) {logger.error("订单处理未知异常: {}", order.getId(), e);return CompletableFuture.failedFuture(e);}}// 批量异步处理订单@Async("batchOrderExecutor")public CompletableFuture<Void> processOrdersBatch(List<Order> orders) {List<CompletableFuture<OrderResult>> futures = orders.stream().map(this::processOrderAsync).collect(Collectors.toList());return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).exceptionally(ex -> {logger.error("批量处理订单异常", ex);return null;});}
}// 配置类
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(10);executor.setMaxPoolSize(20);executor.setQueueCapacity(100);executor.setThreadNamePrefix("Order-Async-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.setWaitForTasksToCompleteOnShutdown(true);executor.setAwaitTerminationSeconds(60);executor.initialize();return executor;}@Bean(name = "batchOrderExecutor")public Executor batchOrderExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(50);executor.setThreadNamePrefix("Batch-Order-");executor.initialize();return executor;}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new OrderAsyncExceptionHandler();}
}// 全局异常处理器
public class OrderAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {private static final Logger logger = LoggerFactory.getLogger(OrderAsyncExceptionHandler.class);@Overridepublic void handleUncaughtException(Throwable ex, Method method, Object... params) {logger.error("异步订单处理异常 - 方法: {}, 参数: {}", method.getName(), Arrays.toString(params), ex);// 发送告警邮件if (ex instanceof CriticalOrderException) {sendCriticalAlert(method, ex, params);}}private void sendCriticalAlert(Method method, Throwable ex, Object... params) {// 实现告警逻辑}
}
喜欢的点个关注,想了解更多的可以关注微信公众号 “Eric的技术杂货库” ,提供更多的干货以及资料下载保存!
相关文章:
SpringBoot异步处理@Async深度解析:从基础到高阶实战
一、异步编程基础概念 1.1 同步 vs 异步 特性同步异步执行方式顺序执行,阻塞调用非阻塞,调用后立即返回线程使用单线程完成所有任务多线程并行处理响应性较差,需等待前任务完成较好,可立即响应新请求复杂度简单直观较复杂&#…...

【生存技能】ubuntu 24.04 如何pip install
目录 原因解决方案说明关于忽略系统路径 在接手一个新项目需要安装python库时弹出了以下提示: 原因 这个报错是因为在ubuntu中尝试直接使用 pip 安装 Python 包到系统环境中,ubuntu 系统 出于稳定性考虑禁止了这种操作 这里的kali是因为这台机器的用户起名叫kali…...

SHAP分析!Transformer-GRU组合模型SHAP分析,模型可解释不在发愁!
SHAP分析!Transformer-GRU组合模型SHAP分析,模型可解释不在发愁! 目录 SHAP分析!Transformer-GRU组合模型SHAP分析,模型可解释不在发愁!效果一览基本介绍程序设计参考资料 效果一览 基本介绍 基于SHAP分析…...
Tcp 通信简单demo思路
Server 端 -------------------------- 初始化部分 ------------------------------- 1.创建监听套接字: 使用socket(协议家族,套接字的类型,0) 套接字类型有 SOCK_STREAM:表示面向连接的套接字(Tcp协议)&…...
MySQL 8.0安装(压缩包方式)
MySQL 8.0安装(压缩包方式) 下载安装包并解压 下载 https://dev.mysql.com/downloads/mysql/可关注“后端码匠”回复“MySQL8”关键字获取 解压(我解压到D:\dev\mysql-8.4.5-winx64目录下) 创建mysql服务 注意,这步之前一定要保证自己电…...
常见标签语言的对比
XML、JSON 和 YAML 是常见的数据序列化格式 相同点 结构化数据表示 三者均支持嵌套结构,能描述复杂的数据层级关系(如对象、数组、键值对)。跨平台兼容性 均为纯文本格式,可被多种编程语言解析,适用于跨系统数据交换…...

知名人工智能AI培训公开课内训课程培训师培训老师专家咨询顾问唐兴通AI在金融零售制造业医药服务业创新实践应用
AI赋能未来工作:引爆效率与价值创造的实战营 AI驱动的工作革命:从效率提升到价值共创 培训时长: 本课程不仅是AI工具的操作指南,更是面向未来的工作方式升级罗盘。旨在帮助学员系统掌握AI(特别是生成式AI/大语言模型…...

Qt Creator 配置 Android 编译环境
Qt Creator 配置 Android 编译环境 环境配置流程下载JDK修改Qt Creator默认android配置文件修改sdk_definitions.json配置修改的内容 Qt Creator配置 异常处理删除提示占用编译报错连接安卓机调试APP闪退 环境 Qt Creator 版本 qtcreator-16.0.1Win10 嗯, Qt这个开发环境有点难…...

智能手表蓝牙 GATT 通讯协议文档
以下是一份适用于智能手表的 蓝牙 GATT 通讯协议文档,适用于 BLE 5.0 及以上标准,兼容 iOS / Android 平台: 智能手表蓝牙 GATT 通讯协议文档 文档版本:V1.0 编写日期:2025年xx月xx日 产品型号:Aurora Wat…...
AWS EC2源代码安装valkey命令行客户端
sudo yum -y install openssl-devel gcc wget https://github.com/valkey-io/valkey/archive/refs/tags/8.1.1.tar.gz tar xvzf 8.1.1.tar.gz cd valkey-8.1.1/ make distclean make valkey-cli BUILD_TLSyes参考 Connecting to nodes...

RT-THREAD RTC组件中Alarm功能驱动完善
使用Rt-Thread的目的为了更快的搭载工程,使用Rt-Thread丰富的组件和第三方包资源,解耦硬件,在更换芯片时可以移植应用层代码。你是要RTT的目的什么呢? 文章项目背景 以STM32L475RCT6为例 RTC使用的为LSE外部低速32 .756k Hz 的…...
MySQL解决主从复制的报错问题
MySQL 8.4 非 GTID 模式部分数据库主从复制指南 在进行MySQL 8.4非GTID模式下部分数据库主从复制时,以下是详细的操作步骤以及对应的执行位置说明,还有报错处理方法介绍: 操作步骤 1. 备份主库指定数据库(db1、db2)…...

用ffmpeg压缩视频参数建议
注意:代码中的斜杠\可以删除 一、基础压缩命令(画质优先) libx265推荐配置 ffmpeg -i input.mp4 -c:v libx265 -crf 25 -preset medium -c:a aac -b:a 128k output.mp4-crf:建议25-28(值越小画质越高) -preset:平…...

输入顶点坐标输出立方体长宽高的神经网络 Snipaste贴图软件安装
写一个神经网络,我输入立方体投影线段的三视图坐标,输出分类和长宽高 放这了明天接着搞 -------------------------------------------- 开搞 然而我的数据是这样的 winget install Snipaste f1启动,双击贴图隐藏 用右边4个数据做输入…...

用python清除PDF文件中的水印(Adobe Acrobat 无法删除)
学校老师发的资料,有时候会带水印,有点强迫症的都想给它去掉。用Adobe Acrobat试了下,检测不到水印,无法删除!分析发现原来这类PDF文件是用word编辑的,其中的水印是加在了页眉中! 自己动手想办法…...
kotlin 数据类
一 kotlin数据类与java普通类区别 Kotlin 的 data class 与 Java 中的普通类(POJO)相比,确实大大减少了样板代码(boilerplate),但它的优势不止于自动生成 getter/setter、copy()、equals()、toString()&am…...
豆瓣电影Top250数据工程实践:从爬虫到智能存储的技术演进(含完整代码)
目录 引言:当豆瓣榜单遇见大数据技术 项目文档 1.1 选题背景 1.2 项目目标 2. 项目概述 2.1 系统架构设计 2.2 技术选型 2.3 项目环境搭建 2.3.1 基础环境准备 2.3.2 爬虫环境配置 2.3.3 Docker安装ES连接Kibana 安装IK插件 2.3.4 vscode依赖服务安装 3. 核心模…...
把Excel数据文件导入到Oracle数据库
数据管理和分析的领域,将Excel中的数据导入到Oracle数据库是一个常见的需求,无论是为了利用Oracle强大的数据处理能力,还是为了实现数据的集中存储和管理,这一过程都需要一定的步骤和技巧,本文将详细介绍如何从Excel导…...
PyTorch API 6 - 编译、fft、fx、函数转换、调试、符号追踪
文章目录 torch.compiler延伸阅读 torch.fft快速傅里叶变换辅助函数 torch.func什么是可组合的函数变换?为什么需要可组合的函数变换?延伸阅读 torch.futurestorch.fx概述编写转换函数图结构快速入门图操作直接操作计算图使用 replace_pattern() 进行子图…...

Dagster Pipes系列-1:调用外部Python脚本
本文是"Dagster Pipes教程"的第一部分,介绍如何通过Dagster资产调用外部Python脚本并集成到数据管道中。首先,创建Dagster资产subprocess_asset,利用PipesSubprocessClient资源执行外部脚本external_code.py,实现跨进程…...

python shutil 指定文件夹打包文件为 zip 压缩包
python shutil 指定文件夹打包文件为 zip 压缩包,具体代码如下: import shutil# 指定要打包的文件夹路径 src_doc ./test# 指定输出的压缩包文件名(不包含扩展名) output_filename testfromat_ zip# 打包并压缩文件夹为 ZIP …...

Webug4.0通关笔记25- 第30关SSRF
目录 一、SSRF简介 1.SSRF原理 2.渗透方法 二、第30关SSRF渗透实战 1.打开靶场 2.渗透实战 (1)Windows靶场修复 (2)Docker靶场修复 (3)获取敏感文件信息 (4)内网端口与服务…...
Android学习总结之线程池篇
一、线程池参数调优实战真题 真题 1:直播 APP 弹幕加载线程池设计 题目描述:直播 APP 需要实时加载弹幕数据(网络请求,IO 密集型),同时渲染弹幕视图(UI 操作需切主线程)࿰…...

OpenCV 中用于背景分割的一个类cv::bgsegm::BackgroundSubtractorLSBP
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::bgsegm::BackgroundSubtractorLSBP 是 OpenCV 中用于背景分割的一个类,它基于局部样本二进制模式(Local Sample Bina…...

MacOS 上构建 gem5
MacOS 中只存在 python3,但是scons 只认 python,不在 系统中创建 软连接,一个是因为比较难操作;另一个是尽量不要更改系统。所以独立构件python 和scons: 1,安装python 下载源代码: Python S…...

认识中间件-以及两个简单的示例
认识中间件-以及两个简单的示例 什么是中间件一个响应处理中间件老朋友 nest g如何使用为某个module引入全局引入编写逻辑一个日志中间件nest g mi 生成引入思考代码进度什么是中间件 官方文档 中间件是在路由处理程序之前调用的函数。中间件函数可以访问请求和响应对象,以及…...

(五)毛子整洁架构(分布式日志/Redis缓存/OutBox Pattern)
文章目录 项目地址一、结构化日志1.1 使用Serilog1. 安装所需要的包2. 注册服务和配置3. 安装Seq服务 1.2 添加分布式id中间件1. 添加中间件2. 注册服务3. 修改Application的LoggingBehavior 二、Redis缓存2.1 添加缓存1. 创建接口ICaching接口2. 实现ICaching接口3. 注册Cachi…...
SQL:MySQL函数:字符串函数
目录 为什么需要字符串函数? 1️⃣ LENGTH(str) — 这个字符串有几个“字节”? 2️⃣ CHAR_LENGTH(str) — 这个字符串有几个“字符”? 3️⃣ TRIM(str) — 把两边的空格剪掉 4️⃣ REPLACE(str, a, b) — 把 a 替换成 b 使用这些函数时…...
DAY04:Vue.js 指令与事件处理深度解析之从基础到实战
1. 指令系统核心概念 1.1 插值表达式与基础指令 Vue.js 的指令系统是其响应式编程模型的核心,我们首先从最基础的插值表达式开始: <div id"app"><!-- 基础文本插值 --><p>{{ message }}</p><!-- JavaScript 表达…...

大模型微调终极方案:LoRA、QLoRA原理详解与LLaMA-Factory、Xtuner实战对比
文章目录 一、微调概述1.1 微调步骤1.2 微调场景 二、微调方法2.1 三种方法2.2 方法对比2.3 关键结论 三、微调技术3.1 微调依据3.2 LoRA3.2.1 原理3.2.2 示例 3.3 QLoRA3.4 适用场景 四、微调框架4.1 LLaMA-Factory4.2 Xtuner4.3 对比 一、微调概述 微调(Fine-tun…...