@Async异步操作及异步线程池
本文为博主原创,转载请注明出处:
@Async 用来实现异步请求操作,使用@Async 注解时,需要同时使用 @EnableAsync 注解,使用 @EnableAsync 注解用于开启异步请求。
如果没有使用 @EnableAsync 注解,则不会开启异步操作,是同步请求。
一。异步方法调用及讲解
使用场景: 当不影响当前主线程的功能,新建线程进行其他功能。比如:异步消息通知,或异步文件上传等等。
封装一个使用 @Async 的service 类,进行测试调用:
package com.example.demo.service.impl; import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service; import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future; @Service
@EnableAsync
public class AsyncServiceImpl { // 异步调用无返回
@Async
public void getAsyncNum() throws InterruptedException {
System.out.println("==AsyncServiceImpl=getAsyncNum==="+Thread.currentThread().getName());
Thread.sleep(3000);
} // 没有使用 @Async 注解,则为同步方法
public Integer getSyncNum(){
System.out.println("==AsyncServiceImpl=getSyncNum==="+Thread.currentThread().getName());
Random random = new Random();
int num = random.nextInt(333333);
return num;
} // 异步调用返回一个Future 类型的参数
@Async
public Future<String> getAsyncFuture() throws InterruptedException {
int thinking = 2;
Thread.sleep(thinking * 1000);
System.out.println("getAsyncFuture==="+Thread.currentThread().getName());
return new AsyncResult<String>("发送消息用了"+thinking+"秒");
} // 异步调用 返回一个 CompletableFuture 类型的参数
@Async
public CompletableFuture<Integer> completableFutureTask() {
System.out.println("---completableFutureTask---"+Thread.currentThread().getName());
Random random = new Random();
int num = random.nextInt(333333);
// 模拟这是一个耗时的任务
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
//返回一个已经用给定值完成的新的CompletableFuture。
return CompletableFuture.completedFuture(num);
}
}
1. 调用一个异步无返回参数的方法
调用一个异步无返回参数的方法,则直接调用既可,异步方法会新开启一个线程,执行异步方法:
@Test
void queryAsync() throws InterruptedException {
System.out.println("----queryAsync--"+Thread.currentThread().getName());
asyncService.getAsyncNum();
System.out.println("==queryAsync=end=");
}
打印的描述为:
可以发现,测试方法会立即结束,异步方法还在执行,相互不影响。
2. 调用同步方法,查看线程:
@Test
void test2(){
System.out.println("---test2---"+Thread.currentThread().getName());
int num = asyncService.getSyncNum();
}
打印结果为:
没有使用 @Async 注解的方法,直接调用,整个方法都是同一线程。
3. 将 上面同步方法不用 @Async 改进为 异步方法,
使用 FutureTask 类封装异步方法,且需新建并启动一个新的Thread,实现异步功能:
@Test
void test3() throws ExecutionException, InterruptedException {
System.out.println("---test3---"+Thread.currentThread().getName());
FutureTask<Integer> task = new FutureTask<>(()->asyncService.getSyncNum());
Thread thread = new Thread(task);
thread.start();
int num = task.get()+ 3;
System.out.println(num);
}
打印的日志为:
通过 FutureTask 与Thread 实现一个异步方法功能,发现方法调用与当前方法是两个线程。
4. @Async 注解进行异步方法及存在返回值使用 AsyncResult 封装
当异步方法存在返回参数时,必须使用Future 类或相关子类进行封装,如果不进行封装,则调用异步方法时,会直接返回null,在解析或使用返回参数时,就会报空指针异常。
使用 @Async 注解进行异步方法,存在返回使,可以使用 AsyncResult 类进行封装。其也是 future 的子类。可以看 getAsyncFuture 中的使用和返回。
@Test
void test4() throws InterruptedException, ExecutionException {
System.out.println("---test4---"+Thread.currentThread().getName());
Future<String> result = asyncService.getAsyncFuture();
String result2 = result.get();
System.out.println(result2);
}
上述测试方法执行打印的日志如下:
调用 getAsyncFuture()异步方法,会新建一个线程执行,且该方法为有参数返回。使用 AsyncResult 与Future 相关封装。获取返回值时使用 get()方法。
该方法会等待异步方法返回结果。
5. 使用 CompletableFuture 类封装异步返回结果。
该类也是 Future 的子类。其使用方法可参考最上面 completableFutureTask()方法。
@Test
void test5() throws ExecutionException, InterruptedException {
System.out.println("---test5---"+Thread.currentThread().getName());
CompletableFuture<Integer> result = asyncService.completableFutureTask();
System.out.println(result.get());
}
进行测试方法调用:
CompletableFuture 与 AsyncResult 使用场景相同,都是使用get()获取返回异步方法返回结果,且会等待异步方法执行结束。
二。 使用 ThreadPoolTaskExecutor 定义异步线程池
package com.example.demo.config; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor; @Configuration
@EnableAsync
public class AsyncConfig {
private static final int CORE_POOL_SIZE = 6;
private static final int MAX_POOL_SIZE = 10;
private static final int QUEUE_CAPACITY = 100; @Bean
public Executor taskExecutor() {
// Spring 默认配置是核心线程数大小为1,最大线程容量大小不受限制,队列容量也不受限制。
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(CORE_POOL_SIZE);
// 最大线程数
executor.setMaxPoolSize(MAX_POOL_SIZE);
// 队列大小
executor.setQueueCapacity(QUEUE_CAPACITY);
// 当最大池已满时,此策略保证不会丢失任务请求,但是可能会影响应用程序整体性能。
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setThreadNamePrefix("My ThreadPoolTaskExecutor-");
executor.initialize();
return executor;
}
}
ThreadPoolTaskExecutor
常见概念:
- Core Pool Size : 核心线程数线程数定义了最小可以同时运行的线程数量。
- Queue Capacity : 当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,信任就会被存放在队列中。
- Maximum Pool Size : 当队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
一般情况下不会将队列大小设为:Integer.MAX_VALUE
,也不会将核心线程数和最大线程数设为同样的大小,这样的话最大线程数的设置都没什么意义了,
你也无法确定当前 CPU 和内存利用率具体情况如何。
如果队列已满并且当前同时运行的线程数达到最大线程数的时候,如果再有新任务过来会发生什么呢?
Spring 默认使用的是 ThreadPoolExecutor.AbortPolicy
。在Spring的默认情况下,ThreadPoolExecutor
将抛出 RejectedExecutionException
来拒绝新来的任务 ,
这代表你将丢失对这个任务的处理。 对于可伸缩的应用程序,建议使用 ThreadPoolExecutor.CallerRunsPolicy
。当最大池被填满时,此策略为我们提供可伸缩队列。
ThreadPoolTaskExecutor
饱和策略定义:
如果当前同时运行的线程数量达到最大线程数量时,ThreadPoolTaskExecutor
定义一些策略:
- ThreadPoolExecutor.AbortPolicy:抛出
RejectedExecutionException
来拒绝新任务的处理。 - ThreadPoolExecutor.CallerRunsPolicy:调用执行自己的线程运行任务。您不会任务请求。但是这种策略会降低对于新任务提交速度,影响程序的整体性能。另外,这个策略喜欢增加队列容量。如果您的应用程序可以承受此延迟并且你不能任务丢弃任何一个任务请求的话,你可以选择这个策略。
- ThreadPoolExecutor.DiscardPolicy: 不处理新任务,直接丢弃掉。
- ThreadPoolExecutor.DiscardOldestPolicy: 此策略将丢弃最早的未处理的任务请求。
测试方法调用异步线程池异步方法:
@Test
void test6() throws InterruptedException {
System.out.println("----test6--"+Thread.currentThread().getName());
for (int i =0;i<6;i++){
asyncService.getAsyncNum();
}
System.out.println("==test6=end=");
}
调用打印的异常日志如下:
@Async异步操作及异步线程池的更多相关文章
- Spring Boot系列二 Spring @Async异步线程池用法总结
1. TaskExecutor Spring异步线程池的接口类,其实质是java.util.concurrent.Executor Spring 已经实现的异常线程池: 1. SimpleAsyncT ...
- spring boot:使用async异步线程池发送注册邮件(spring boot 2.3.1)
一,为什么要使用async异步线程池? 1,在生产环境中,有一些需要延时处理的业务场景: 例如:发送电子邮件, 给手机发短信验证码 大数据量的查询统计 远程抓取数据等 这些场景占用时间较长,而用户又没 ...
- 使用C++11实现一个半同步半异步线程池
前言 C++11之前我们使用线程需要系统提供API.posix线程库或者使用boost提供的线程库,C++11后就加入了跨平台的线程类std::thread,线程同步相关类std::mutex.std ...
- SpringBoot使用异步线程池实现生产环境批量数据推送
前言 SpringBoot使用异步线程池: 1.编写线程池配置类,自定义一个线程池: 2.定义一个异步服务: 3.使用@Async注解指向定义的线程池: 这里以我工作中使用过的一个案例来做描述,我所在 ...
- 使用C++11 开发一个半同步半异步线程池
摘自:<深入应用C++11>第九章 实际中,主要有两种方法处理大量的并发任务,一种是一个请求由系统产生一个相应的处理请求的线程(一对一) 另外一种是系统预先生成一些用于处理请求的进程,当请 ...
- c++11 实现半同步半异步线程池
感受: 随着深入学习,现代c++给我带来越来越多的惊喜- c++真的变强大了. 半同步半异步线程池: 事实上非常好理解.分为三层 同步层:通过IO复用或者其它多线程多进程等不断的将待处理事件加入到队列 ...
- (原创)C++半同步半异步线程池2
(原创)C++半同步半异步线程池 c++11 boost技术交流群:296561497,欢迎大家来交流技术. 线程池可以高效的处理任务,线程池中开启多个线程,等待同步队列中的任务到来,任务到来多个线程 ...
- Python+Requests+异步线程池爬取视频到本地
1.本次项目为获取梨视频中的视频,再使用异步线程池下载视频到本地 2.获取视频时,其地址中的Url是会动态变化,不播放时src值为图片的地址,播放时src值为mp4格式 3.查看视频链接是否存在aja ...
- Springboot的异步线程池
1:定义线程池 @EnableAsync @Configuration class TaskPoolConfig { @Bean("taskExecutor") public Ex ...
- Spring boot 异步线程池
package com.loan.msg.config; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandle ...
随机推荐
- 不会使用 EF Core 的 Code First 模式?来看看这篇文章,手把手地教你
EF Core Code First 是什么 Code First 是 Entity Framework Core (简称 EF Core) 的一种开发模式,它允许开发人员使用纯粹的代码来定义数据模型 ...
- NC65二开经验总结
公式相关 1.显示公式没执行 列表界面显示,卡片界面不显示: Handler的onBoCard执行: getBillCardPanel().execHeadLoadFormulas(); Contro ...
- 从零玩转第三方登录之WeChat公众号扫码关注登陆 -wechatgzh
title: 从零玩转第三方登录之WeChat公众号扫码关注登陆 date: 2022-09-27 22:46:53.362 updated: 2023-03-30 13:28:41.359 url: ...
- STM32CubeMX教程2 GPIO输出 - 点亮LED灯
1.准备材料 开发板(STM32F407G-DISC1) ST-LINK/V2驱动 STM32CubeMX软件(Version 6.10.0) keil µVision5 IDE(MDK-Arm) 2 ...
- 良心国产工具,比Xshell好用还免费!
使用或维护Linux系统的都知道,我们日常对服务器的操作,一般都会借助SSH工具远程登录到服务器之后进行操作.常用的SSH工具有不少,比如:Xshell.Putty.SSH Secure Shell ...
- Java面试必考:什么是字节码?采用字节码的好处?
Java面试必考:什么是字节码?采用字节码的好处? 于哥你好,最近在java面试中被问答到什么是字节码?采用字节码的好处是什么?瞬间懵了,如果你连这个都不知道,我保证你面试GG! 首先说下Java的优 ...
- JavaImprove--Lesson04--LocalDateTime,ZoneId,Instant,DateTimeFormatter
一.LocalDateTime LocalDateTime是JDK8的新时间特性,它解决了Date类和Calender类的很多不足,如使用不方便,线程不安全,以及获取时间戳只能拿到毫秒而不能拿到纳秒等 ...
- .NET技术分享日活动-202202
2022年02月19日下午,个人组织举办了山东地区的第四次.NET技术分享日活动.主要包含.NET常用技术.低代码.大前端.大数据和工作流等五个技术领域. 本次技术分享日活动面向了山东地区广大的.NE ...
- 干货分享丨轻松玩转 Huawei LiteOS 传感框架
摘要:LiteOS传感框架将物联网终端设备上不同类型的传感器统一管理,通过抽象不同类型传感器接口,屏蔽其硬件细节,做到"硬件"无关性,非常方便于物联网设备的开发.维护和功能扩展. ...
- 一文总结GaussDB通信原理知识
摘要:从发展历程到通信模型设计,到你了解一下GaussDB通信原理知识. MPPDB通信库发展历程 Postgres-XC 方法:采用libpq通信库实现CN和DN之间的连接,CN负责计算,DN仅进行 ...