@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 ...
随机推荐
- MySQL运维15-一主一从读写分离
一.读写分离介绍 读写分离,是把数据库的读和写分开操作,以应对不同的数据库服务器.主数据库提供写操作,从数据库提供读操作,这样能有效的减轻单台数据库的压力. 二.一主一从原理 MySQL的主从复制是基 ...
- 深入理解 Docker 核心原理:Namespace、Cgroups 和 Rootfs
通过这篇文章你可以了解到 Docker 容器的核心实现原理,包括 Namespace.Cgroups.Rootfs 等三个核心功能. 如果你对云原生技术充满好奇,想要深入了解更多相关的文章和资讯,欢迎 ...
- Python——第一章:循环语句while
循环语句可以让我们的代码重复的去执行 while循环: while 条件: 代码 过程: 判断while循环的条件是否为真, 如果真, 执行代码. 然后再次判断条件.....直到条件为假 ...
- 一篇文章彻底搞懂TiDB集群各种容量计算方式
背景 TiDB 集群的监控面板里面有两个非常重要.且非常常用的指标,相信用了 TiDB 的都见过: Storage capacity:集群的总容量 Current storage size:集群当前已 ...
- Golang 命名返回值和普通返回值
1.概述 在Go语言中,函数可以有命名返回值和普通(匿名)返回值.命名返回值会被视为定义在函数顶部的变量,并且在使用 return 语句返回时,不再必须在其后面指定参数名,也就是支持"裸&q ...
- JavaScript异步编程4——Promise错误处理
目录 1. 概述 2. 详论 3. 参考 1. 概述 在上一篇文章<JavaScript异步编程3--Promise的链式使用>中,通过Promise的链式使用,避免程序中多次嵌套回调(回 ...
- 云小课 | SA基线检查—给云服务的一次全面“体检”
阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要: 华为云态势感知( ...
- java并发编程(1):Java多线程-基本线程类-基础知识复习笔记
复习资料:<同步与异步:并发/并行/进程/线程/多cpu/多核/超线程/管程 > 基本线程类 基本线程类 基本线程类指的是Thread类,Runnable接口,Callable接口 继承T ...
- GIS拓扑讲解点线面几何体的拓扑关系判断及运算分析_turf案例
Turf.js简介 Turf.js是JavaScript 空间分析库,由Mapbox 提供,Turf 实现了 空间分析操作,例如生成缓冲区.计算等高线,建立 TIN 等: 空间几何对象关系的计算,点 ...
- 再谈BOM和DOM(2):DOM节点层次/属性/选择器/节点关系/操作详解
DOM模型将整个文档(XML文档和HTML文档)看成一个树形结构,并用document对象表示该文档. 根据W3C DOM规范,DOM是HTML与XML的应用编程接口(API),DOM将整个页面映射为 ...