@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 ...
随机推荐
- Java项目整合短信验证码
一.开通短信服务 本来想整合阿里云短信服务的,可是签名一直审核不过,所以在阿里云的云市场找到了一个替代产品(sddx) 接下来小伙伴们按照自己的经济实力购买或者用免费的5条(我就是用免费的5条了) 购 ...
- Charles对Android手机Https请求的抓包
Charles对Android手机Https请求的抓包 • 前情提要: 本文只是对android手机进行抓包的描述,由于android手机系统原因,android7.0系统及以上需要在app中配置证书 ...
- MyBatis中获取参数值的两种方式:${} 和 #{},以及它们之间区别是什么?
MyBatis中获取参数值的两种方式 ${}:的本质就是字符串拼接 #{}:的本质就是占位符赋值 ① 使用${}占位符,在字符串拼接的方式拼接sql,若为字符串类型或为日期类型的字段进行赋值时,需要手 ...
- C# 获取另一程序控件,改变值,触发事件
[DllImport("User32.dll", EntryPoint = "FindWindow")]private static extern IntPtr ...
- C语言基础之因子分解
要求: 从键盘输入一个正整数,然后将该整数分解为1和各个质因子的相乘,如果输入的整数本身就是质数,则应分解为1和该数本身相乘. 输出格式: 因子分解,因子由小到大输出. 如:1* 2* 2* 3 代码 ...
- C语言实现链表与文件的存取
作者:柠檬i,学习C时长两个月半的个人练习生 第一次写文章,难免有些不足,请多多包涵. 本程序主要功能是建立链表,然后把链表数据存储到文件中,然后把文件数据存储到数组中并输出. 不多说了,放代码. 此 ...
- javacv图片美颜处理,视频美颜处理
javacv图片美颜处理,视频美颜处理 国产剧明星演戏自带十级滤镜,是众所周知的秘密: 使用opencv也能实现一定的美颜效果: 一.图片美颜 代码 package top.lingkang.test ...
- Java 在PDF中绘制形状(基于Spire.Cloud.SDK for Java)
Spire.Cloud.SDK for Java提供了pdfPathApi接口可用于在PDF文档中绘制形状(或图形),如绘制线条形状drawLine().绘制矩形形状drawRectanglef(), ...
- 什么是VXLAN?为什么需要VXLAN?
摘要:本文介绍了什么是VXLAN,以及VXLAN的基本概念和工作原理,包括:为什么需要VXLAN?VXLAN与VLAN之间有啥不同?什么是VTEP?什么是VNI?VXLAN报文是如何封装的?VXLAN ...
- OCR性能优化:从神经网络到橡皮泥
摘要:在这个算力还可以的时代,我们的研究人员一方面致力于不断地去研究各中不同的场景中的的通用网络,一方面致力于优化神经网络的学习方式,这些都是在试图化减少AI需要的算力资源. 本文分享自华为云社区&l ...