Java异步注解@Async详解
一、@Async注解
@Async的作用就是异步处理任务。
- 在方法上添加@Async,表示此方法是异步方法;
- 在类上添加@Async,表示类中的所有方法都是异步方法;
- 使用此注解的类,必须是Spring管理的类;
- 需要在启动类或配置类中加入@EnableAsync注解,@Async才会生效;
在使用@Async时,如果不指定线程池的名称,也就是不自定义线程池,@Async是有默认线程池的,使用的是Spring默认的线程池SimpleAsyncTaskExecutor。
默认线程池的默认配置如下:
- 默认核心线程数:8;
- 最大线程数:Integet.MAX_VALUE;
- 队列使用LinkedBlockingQueue;
- 容量是:Integet.MAX_VALUE;
- 空闲线程保留时间:60s;
- 线程池拒绝策略:AbortPolicy;
从最大线程数可以看出,在并发情况下,会无限制的创建线程。
也可以通过yml重新配置:
spring:task:execution:pool:max-size: 10core-size: 5keep-alive: 3squeue-capacity: 1000thread-name-prefix: my-executor
也可以自定义线程池,下面通过简单的代码来实现以下@Async自定义线程池。
二、代码实例
Spring为任务调度与异步方法执行提供了注解@Async支持,通过在方法上标注@Async注解,可使得方法被异步调用。在需要异步执行的方法上加入@Async注解,并指定使用的线程池,当然可以不指定,直接写@Async。
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.0.1-jre</version>
</dependency>
2、配置类
package com.jack.config;import com.google.common.util.concurrent.ThreadFactoryBuilder;
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.*;@EnableAsync// 支持异步操作
@Configuration
public class AsyncTaskConfig {/*** com.google.guava中的线程池* @return*/@Bean("my-executor")public Executor firstExecutor() {ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("my-executor").build();// 获取CPU的处理器数量int curSystemThreads = Runtime.getRuntime().availableProcessors() * 2;ThreadPoolExecutor threadPool = new ThreadPoolExecutor(curSystemThreads, 100,200, TimeUnit.SECONDS,new LinkedBlockingQueue<>(), threadFactory);threadPool.allowsCoreThreadTimeOut();return threadPool;}/*** Spring线程池* @return*/@Bean("async-executor")public Executor asyncExecutor() {ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();// 核心线程数taskExecutor.setCorePoolSize(10);// 线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程taskExecutor.setMaxPoolSize(100);// 缓存队列taskExecutor.setQueueCapacity(50);// 空闲时间,当超过了核心线程数之外的线程在空闲时间到达之后会被销毁taskExecutor.setKeepAliveSeconds(200);// 异步方法内部线程名称taskExecutor.setThreadNamePrefix("async-executor-");/*** 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略* 通常有以下四种策略:* ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。* ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。* ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)* ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute() 方法,直到成功*/taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());taskExecutor.initialize();return taskExecutor;}
}
3、controller
package com.jack.controller;import com.nezha.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/test")
public class UserController {private static final Logger logger = LoggerFactory.getLogger(UserController.class);@Autowiredprivate UserService userService;@GetMapping("asyncTest")public void asyncTest() {logger.info("杰克真帅");userService.asyncTest();asyncTest2();logger.info("杰克编程,每日更新Java干货");}@Async("my-executor")public void asyncTest2() {logger.info("同文件内执行执行异步任务");}
}
4、service
package com.nezha.service;public interface UserService {// 普通方法void test();// 异步方法void asyncTest();
}
service实现类
package com.jack.service;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements UserService {private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);@Overridepublic void test() {logger.info("执行普通任务");}@Async("my-executor")@Overridepublic void asyncTest() {logger.info("执行异步任务");}
}
三、发现同文件内执行异步任务,还是一个线程,没有实现@Async效果,为什么?
查到了@Async失效的几个原因:
- 注解@Async的方法不是public方法;
- 注解@Async的返回值只能为void或Future;
- 注解@Async方法使用static修饰也会失效;
- 没加@EnableAsync注解;
- 调用方和@Async不能在一个类中;
- 在Async方法上标注@Transactional是没用的,但在Async方法调用的方法上标注@Transcational是有效的;
这里就不一一演示了,有兴趣的小伙伴可以研究一下。
四、配置中分别使用了ThreadPoolTaskExecutor和ThreadPoolExecutor,这两个有啥区别?
ThreadPoolTaskExecutor是spring core包中的,而ThreadPoolExecutor是JDK中的JUC。ThreadPoolTaskExecutor是对ThreadPoolExecutor进行了封装。
1、initialize()
查看一下ThreadPoolTaskExecutor 的 initialize()方法
public abstract class ExecutorConfigurationSupport extends CustomizableThreadFactoryimplements BeanNameAware, InitializingBean, DisposableBean {.../*** Set up the ExecutorService.*/public void initialize() {if (logger.isInfoEnabled()) {logger.info("Initializing ExecutorService" + (this.beanName != null ? " '" + this.beanName + "'" : ""));}if (!this.threadNamePrefixSet && this.beanName != null) {setThreadNamePrefix(this.beanName + "-");}this.executor = initializeExecutor(this.threadFactory, this.rejectedExecutionHandler);}/*** Create the target {@link java.util.concurrent.ExecutorService} instance.* Called by {@code afterPropertiesSet}.* @param threadFactory the ThreadFactory to use* @param rejectedExecutionHandler the RejectedExecutionHandler to use* @return a new ExecutorService instance* @see #afterPropertiesSet()*/protected abstract ExecutorService initializeExecutor(ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler);...
}
2、initializeExecutor抽象方法
再查看一下initializeExecutor抽象方法的具体实现类,其中有一个就是ThreadPoolTaskExecutor类,查看它的initializeExecutor方法,使用的就是ThreadPoolExecutor。
public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupportimplements AsyncListenableTaskExecutor, SchedulingTaskExecutor {...@Overrideprotected ExecutorService initializeExecutor(ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);ThreadPoolExecutor executor;if (this.taskDecorator != null) {executor = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,queue, threadFactory, rejectedExecutionHandler) {@Overridepublic void execute(Runnable command) {Runnable decorated = taskDecorator.decorate(command);if (decorated != command) {decoratedTaskMap.put(decorated, command);}super.execute(decorated);}};}else {executor = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,queue, threadFactory, rejectedExecutionHandler);}if (this.allowCoreThreadTimeOut) {executor.allowCoreThreadTimeOut(true);}this.threadPoolExecutor = executor;return executor;}...}
因此可以了解到ThreadPoolTaskExecutor是对ThreadPoolExecutor进行了封装。
五、核心线程数
配置文件中的线程池核心线程数为何配置为
// 获取CPU的处理器数量
int curSystemThreads = Runtime.getRuntime().availableProcessors() * 2;
Runtime.getRuntime().availableProcessors()获取的是CPU核心线程数,也就是计算资源。
- CPU密集型,线程池大小设置为N,也就是和cpu的线程数相同,可以尽可能地避免线程间上下文切换,但在实际开发中,一般会设置为N+1,为了防止意外情况出现线程阻塞,如果出现阻塞,多出来的线程会继续执行任务,保证CPU的利用效率。
- IO密集型,线程池大小设置为2N,这个数是根据业务压测出来的,如果不涉及业务就使用推荐。
在实际中,需要对具体的线程池大小进行调整,可以通过压测及机器设备现状,进行调整大小。
如果线程池太大,则会造成CPU不断的切换,对整个系统性能也不会有太大的提升,反而会导致系统缓慢。
六、线程池执行过程
最后
喜欢博主的文章记得点赞、收藏、评论喔~
相关文章:

Java异步注解@Async详解
一、Async注解 Async的作用就是异步处理任务。 在方法上添加Async,表示此方法是异步方法;在类上添加Async,表示类中的所有方法都是异步方法;使用此注解的类,必须是Spring管理的类;需要在启动类或配置类中…...

macOS Big Sur 11.7.5 (20G1225) 正式版 ISO、PKG、DMG、IPSW 下载
本站提供的 macOS Big Sur 软件包,既可以拖拽到 Applications(应用程序)下直接安装,也可以制作启动 U 盘安装,或者在虚拟机中启动安装。 2023 年 3 月 27 日 (北京时间 28 日凌晨),…...

硬件语言Verilog HDL牛客刷题day02 组合逻辑部分
1.VL11 4位数值比较器电路 1.题目: 某4位数值比较器的功能表如下。请用Verilog语言采用门级描述方式,实现此4位数值比较器。 2.解题代码: timescale 1ns/1nsmodule comparator_4(input [3:0] A ,input [3:0] B ,output …...

【LM401】ADC采集代码解读
本文主要实现基于LM401模组,,测试ADC低功耗采集,详细解析代码基于计算方式 对于小白理解ADC有更详细的理解 【LM401】ADC采集代码解读1. 单片机ADC与DAC简单理解2. 模组ADC通道介绍3. ADC初始化4. 采集值的计算5.测试结果硬件基于易智联的LM401的LoRa模组…...
CSDN 编程竞赛四十期题解
竞赛总览 CSDN 编程竞赛四十期:比赛详情 (csdn.net) 竞赛题解 题目1、小鱼的航程 有一只小鱼,它上午游泳150公里,下午游泳100公里,晚上和周末都休息(实行双休日)。假设从周x(1<x<7)开…...

【TypeScript学习之路】泛型
【TypeScript学习之路】泛型 文章目录【TypeScript学习之路】泛型写在前面前言一、认识泛型1.1 什么是泛型1.2 泛型函数的使用二、泛型接口与泛型类2.1 泛型接口2.2 泛型类三、泛型约束写在前面 🤗这里是前端程序员小张! 🌻人海茫茫ÿ…...

数据分析学习项目:东京奥运会跳水评论分析
“中国跳水梦之队” ————有关东京奥运会跳水评论分析 导语 第32届夏季奥林匹克运动会于2021年07月23日-2021年08月08日在日本东京举办。 四年一届的奥运会可以说是世界瞩目的盛会,奥运健儿们在赛场上的精神风貌不只是代表了他们自身的运动精神,更昭…...
Winform/Csharp中使用Linq的Where条件筛选、Select字段映射(左外连接并设置无匹配时默认值)、OrderBy(排序并自定义排序规则)
场景 Java8新特性-Stream对集合进行操作的常用API: Java8新特性-Stream对集合进行操作的常用API_streamapi操作集合_霸道流氓气质的博客-CSDN博客 上面讲的是在Java中使用Stream中对集合的常用操作。 在C#中Linq是有对应的类似的api。 完整和详细的用法可自行查…...
Linux-常用的Shell命令
文章目录前言常用的Shell命令文件和目录管理查看文件、目录信息查看文件内容查看文件类型查找文件查找内容查看目录大小创建文件删除文件拷贝文件移动文件创建目录删除目录拷贝目录压缩文件解压文件路径相关操作目录切换显示当前路径用户、用户组管理创建用户删除用户创建用户组…...

Go语言基础:数组定义及循环遍历
前言 大家好,我是沐风晓月,本文go语言入门-掌握go语言函数收录于《go语言学习专栏》专栏,此专栏带你从零开始学习go语言,持续更新中,欢迎点赞收藏。 🏠个人主页:我是沐风晓月 🧑个人…...

【树与二叉树】二叉树顺序结构实现以及堆的概念及结构--详解介绍
📝个人主页:Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:数据结构 🎯长路漫漫浩浩,万事皆有期待 文章目录1. 二叉树顺序结构2.…...

天狗实战(二)SpringBoot API开发详解 --SpringMVC注解+封装结果+支持跨域+打包(下)
本文目录 前言专栏介绍一、创建SpringBoot项目1.1 添加springboot依赖1.2 创建启动类1.3 创建控制器类1.4 Run 或 Debug二、开发图书管理API2.1 web层BookAdminControllerBookVO2.2 service层BookServiceBookServiceImplBookBO2.3 dal层...

实验一 Windows系统安全实验【网络安全】
实验一 Windows系统安全实验【网络安全】前言推荐实验一 Windows系统安全实验3.1 帐户和口令的安全设置3.1.1 实验目的3.1.2 实验环境3.1.3 实验内容和步骤1. 删除不再使用的帐户并禁用guest帐户2.启用密码策略和帐户锁定策略3.查看“用户权限分配”4.查看“用户组权限分配”5.…...

蓝桥杯正确的解题姿势
在做算法题的过程中最忌讳的就是上来就一顿乱敲,一开始我就是这样,但随着不断的刷题和老师的指导,总结了自己的刷题方法 示例题目 三角回文数 问题描述 对于正整数 n, 如果存在正整数 k使得 n123...kk(k1)/2 , 则 n 称为三角数。例如, 66066 …...
【mysql】性能优化
目录一、硬件与操作系统二、架构设计层面的优化三、mysql程序配置优化四、mysql执行优化一、硬件与操作系统 1.使用高性能cpu,提高计算能力 2.增大可用内存,提高读取能力 3.提高硬盘的读写速度,使用专用的固态硬盘 4.增大网络带宽,…...
Jupyter安装与远程使用过程记录
Jupyter安装与远程使用过程记录 文章目录Jupyter安装与远程使用过程记录Jupyter在线试用在服务器上安装Jupyter Notebook配置服务器远程连接首先保证ip地址连通性其次开启服务器访问端口然后在服务器启动服务最后测试连通性后续使用教程Jupyter在线试用 官网适用,感…...
Swift入门
基本数据类型 Int、UInt:整数型、非负整数Float、Double:单精度浮点数、双精度浮点数Bool:布偶值String、Character:字符串、字符 其他类型 Array, Dictionary:数组、字典StructClassvar:变量let&#x…...

【HashMap】jdk1.8中HashMap的插入扩容源码学习分析
jdk1.8中HashMap的插入扩容源码学习分析 一、成员变量 首先介绍HashMap中各个成员变量的作用,在HashMap中有以下成员变量 size记录了HashMap中键值对的个数 loadFactor(加载因子)用来决定size达到容量的百分之多少时触发扩容机制 默认是0…...

Linux编译器-gcc/g++ 使用
在介绍gcc/g的使用前我们先了解一下两者的不同 gcc时主要编译c语言,而g主要编译c的,但是两者的选项是相同的,因此我们以gcc和c语言为例来讲解。背景知识 gcc和g都是编译器其核心作用将文本类文件翻译成二进制可执行 那么其过程是怎样的&…...

网络安全专家最爱用的9大工具
网络安全专家,不是你认为的那种搞破坏的 “黑客”。网络安全专家,即 “ethical hackers”,是一群专门模拟网络安全专家攻击,帮助客户了解自己网络的弱点,并为客户提出改进建议的网络安全专家。 网络安全专家在工作中&a…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...