@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 ...
随机推荐
- Python 潮流周刊第 31 期(摘要)
本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...
- MybatisPlus查询时过滤不需要的字段~
解释一下:乍一看标题可能有点懵~,其实就是想查询的时候过滤掉某些字段 例如: select name,email,password from user; --改为-> select name,e ...
- 华企盾DSC防泄密客户端安装卡住
解决方法: 1.安装了杀毒或者同行软件,退出后安装不行的话卸载掉 2.注册表编辑被管理员禁用,导致客户端安装时一直卡住,启用注册表编辑(后续2021.6.21后发布的版本可直接安装) 由于安装了卡巴斯 ...
- 若依vue启动报Error: error:0308010C:digital envelope routines::unsupported
解决:若依vue启动报Error: error:0308010C:digital envelope routines::unsupported 1.描述: 问题产生原因是因为 node.js V17版 ...
- DC-3
DC-3 前言:这个DC系列去年就做完了,但是因为那时候visualbox老崩搞得头大,一直漏了DC-3没做.现在重新搞好了来完结这个系列 扫存活的主机,显示只开了80 扫了一下目录,看了几个没有什么 ...
- 基于Docker 部署 Seafile+OnlyOffice+Wiki插件
原文:基于 Docker 部署 SeafilePro + OnlyOffice(CentOS版) 官方文档:用 Docker 部署 Seafile 服务 CentOS 服务器 基于 Docker 部署 ...
- 快速入门Mybatis完成基本CURD(注解实现)
一.什么是Mybatis? MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射. MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集. MyB ...
- 大地经纬度坐标系与Web墨卡托坐标系的转换
目录 1. 概述 2. 实现 3. 参考 1. 概述 我在<大地经纬度坐标与地心地固坐标的的转换>这篇文章中已经论述了大地坐标系/地理坐标系的概念,简单来说就是由经度.纬度以及高程(BLH ...
- gh-pages在线演示踩的坑
git在线演示 1.新建一个gh-pages分支 2.打包好的dist上传到分支里 3.访问:https://[用户名].github.io/[项目名]/dist ( 会自动访问dist下的index ...
- 从部署和运维说说DLI(1)
DLI是支持多模引擎的Serverless大数据计算服务,其很好的实现了Serverless的特性: 1. 弱化了存储和计算之间的联系: 2. 代码的执行不再需要手动分配资源: 3. 按使用量计费 ...