点击上方“方志朋”,选择“设为星标”

回复”666“获取新整理的面试资料

作者:Muscleheng

blog.csdn.net/Muscleheng/article/details/81409672

前言

最近在做订单模块,用户购买服务类产品之后,需要进行预约,预约成功之后分别给商家和用户发送提醒短信。考虑发短信耗时的情况所以我想用异步的方法去执行,于是就在网上看见了Spring的@Async了。

但是遇到了许多问题,使得@Async无效,也一直没有找到很好的文章去详细的说明@Async的正确及错误的使用方法及需要注意的地方,这里简单整理了一下遇见的问题,Sring是以配置文件的形式来开启@Async,而SpringBoot则是以注解的方式开启。

我们可以使用springBoot默认的线程池,不过一般我们会自定义线程池(因为比较灵活),配置方式有:

  1. 使用 xml 文件配置的方式

  2. 使用Java代码结合@Configuration进行配置(推荐使用)

下面分别实现两种配置方式

第一步、配置@Async

一、springBoot启动类的配置:

在Spring Boot的主程序中配置@EnableAsync,如下所示:

@ServletComponentScan
@SpringBootApplication
@EnableAsync
public class ClubApiApplication {public static void main(String[] args) {SpringApplication.run(ClubApiApplication.class, args);}
}

二、Spring XML的配置方式:

1.applicationContext.xml同目录下创建文件threadPool.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:task="http://www.springframework.org/schema/task"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"><!-- 开启异步,并引入线程池 --><task:annotation-driven executor="threadPool" /><!-- 定义线程池 --><bean id="threadPool"class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"><!-- 核心线程数,默认为1 --><property name="corePoolSize" value="10" /><!-- 最大线程数,默认为Integer.MAX_VALUE --><property name="maxPoolSize" value="50" /><!-- 队列最大长度,一般需要设置值>=notifyScheduledMainExecutor.maxNum;默认为Integer.MAX_VALUE --><property name="queueCapacity" value="100" /><!-- 线程池维护线程所允许的空闲时间,默认为60s --><property name="keepAliveSeconds" value="30" /><!-- 完成任务自动关闭 , 默认为false--><property name="waitForTasksToCompleteOnShutdown" value="true" /><!-- 核心线程超时退出,默认为false --><property name="allowCoreThreadTimeOut" value="true" /><!-- 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者 --><property name="rejectedExecutionHandler"><!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 --><!-- CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 --><!-- DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行 --><!-- DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 --><bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" /></property></bean>
</beans>

2.然后在applicationContext.xml中引入threadPool.xml:<import resource="threadPool.xml" />

<!--如果不使用自定义线程池,可以直接使用下面这段标签-->
<!--
<task:executor id="WhifExecutor" pool-size="10"/>
-->
<import resource="threadPool.xml" />
<task:annotation-driven executor="WhifExecutor" />

第二步:创建两个异步方法的类,如下所示:

第一个类(这里模拟取消订单后发短信,有两个发送短信的方法):

@Service
public class TranTest2Service {// 发送提醒短信 1@Asyncpublic void sendMessage1() throws InterruptedException {System.out.println("发送短信方法---- 1   执行开始");Thread.sleep(5000); // 模拟耗时System.out.println("发送短信方法---- 1   执行结束");}// 发送提醒短信 2@Asyncpublic void sendMessage2() throws InterruptedException {System.out.println("发送短信方法---- 2   执行开始");Thread.sleep(2000); // 模拟耗时System.out.println("发送短信方法---- 2   执行结束");}
}

第二个类。调用发短信的方法 (异步方法不能与被调用的异步方法在同一个类中,否则无效):

@Service
public class OrderTaskServic {@Autowiredprivate TranTest2Service tranTest2Service;// 订单处理任务public void orderTask() throws InterruptedException {this.cancelOrder(); // 取消订单tranTest2Service.sendMessage1(); // 发短信的方法   1tranTest2Service.sendMessage2(); // 发短信的方法  2}// 取消订单public void cancelOrder() throws InterruptedException {System.out.println("取消订单的方法执行------开始");System.out.println("取消订单的方法执行------结束 ");}}

经过测试得到如下结果:

1.没有使用@Async

2.使用了@Async

可以看出,没有使用@Async方式实现的发送短信是同步执行的,意思就是说第一条发送之后再发送第二条,第二条发送成功之后再给用户提示,这样显然会影响用户体验,再看使用了@Async实现的,在执行第一个发送短信方法之后马上开启另一个线程执行第二个方法,显然这样我们的处理速度回快很多。

使用Java代码结合@Configuration注解的配置方式(推荐使用)

1. 新建一个配置类

package com.boot.common.conf;import java.util.concurrent.ThreadPoolExecutor;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;/*** 线程池配置* @author zhh**/
@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {/** *   默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,*    当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;*  当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝 *//** 核心线程数(默认线程数) */private static final int corePoolSize = 20;/** 最大线程数 */private static final int maxPoolSize = 100;/** 允许线程空闲时间(单位:默认为秒) */private static final int keepAliveTime = 10;/** 缓冲队列大小 */private static final int queueCapacity = 200;/** 线程池名前缀 */private static final String threadNamePrefix = "Async-Service-";@Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名public ThreadPoolTaskExecutor taskExecutor(){ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(corePoolSize);   executor.setMaxPoolSize(maxPoolSize);executor.setQueueCapacity(queueCapacity);executor.setKeepAliveSeconds(keepAliveTime);executor.setThreadNamePrefix(threadNamePrefix);// 线程池对拒绝任务的处理策略// CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 初始化executor.initialize();return executor;}
}

2.创建两个异步方法的类(和之前的类类似仅仅是方法上注解不一样),如下所示:

第一个类(这里模拟取消订单后发短信,有两个发送短信的方法):

package com.boot.test1.service;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;@Service
public class TranTest2Service {Logger log = LoggerFactory.getLogger(TranTest2Service.class);// 发送提醒短信 1@PostConstruct // 加上该注解项目启动时就执行一次该方法@Async("taskExecutor")public void sendMessage1() throws InterruptedException {log.info("发送短信方法---- 1   执行开始");Thread.sleep(5000); // 模拟耗时log.info("发送短信方法---- 1   执行结束");}// 发送提醒短信 2@PostConstruct // 加上该注解项目启动时就执行一次该方法@Async("taskExecutor")public void sendMessage2() throws InterruptedException {log.info("发送短信方法---- 2   执行开始");Thread.sleep(2000); // 模拟耗时log.info("发送短信方法---- 2   执行结束");}
}

代码中的 @Async("taskExecutor") 对应我们自定义线程池中的 @Bean("taskExecutor") ,表示使用我们自定义的线程池。

第二个类。调用发短信的方法 (异步方法不能与被调用的异步方法在同一个类中,否则无效):

@Service
public class OrderTaskServic {@Autowiredprivate TranTest2Service tranTest2Service;// 订单处理任务public void orderTask() throws InterruptedException {this.cancelOrder(); // 取消订单tranTest2Service.sendMessage1(); // 发短信的方法   1tranTest2Service.sendMessage2(); // 发短信的方法  2}// 取消订单public void cancelOrder() throws InterruptedException {System.out.println("取消订单的方法执行------开始");System.out.println("取消订单的方法执行------结束 ");}}

运行截图:

注意看,截图中的 [nio-8090-exec-1] 是Tomcat的线程名称

[Async-Service-1]、[Async-Service-2]表示线程1和线程2 ,是我们自定义的线程池里面的线程名称,我们在配置类里面定义的线程池前缀:

private static final String threadNamePrefix = "Async-Service-"; // 线程池名前缀,说明我们自定义的线程池被使用了。

注意事项

如下方式会使@Async失效

  • 异步方法使用static修饰

  • 异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类

  • 异步方法不能与被调用的异步方法在同一个类中

  • 类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象

  • 如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解

热门内容:

  • 看看,这些细节上的坑,你犯了多少?

  • 老大说,网上这种获取真实IP地址的方法不对,我不信...

  • AJAX 请求真的不安全么?

  • 你知道为什么Java的main方法必须是public static void?

  • 谈谈中间件开发,给想从事中间件开发的同学

  • 大年夜排查bug:竟然是同事把Redis用成这鬼样子,坑了我

  • 代码神器:拒绝重复编码,这款IDEA插件了解一下.....

  • 问题很难,要慌,redis到底单线程还是多线程

  • SpringBoot实现过滤器、拦截器与切片

  • 阿里面试官:分别说说微信和淘宝扫码登录背后的实现原理?

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

明天见(。・ω・。)ノ♡

SpringBoot线程池的创建、@Async配置步骤及注意事项相关推荐

  1. Spring Boot 线程池的创建、@Async 配置步骤及注意事项

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:终于放弃了单调的swagger-ui了,选择了这款神器-knife4j个人原创+1博客:点击前往,查看更多 作者 ...

  2. Spring及SpringBoot @Async配置步骤及注意事项

    前言 最近在做一个用户反馈的功能,就是当用户反馈意见或建议后服务端将意见保存然后发邮件给相关模块的开发者.考虑发邮件耗时的情况所以我想用异步的方法去执行,于是就开始研究Spring的@Async了.但 ...

  3. SpringBoot线程池ThreadPoolTaskExecutor和@Async异步方法浅解及代码应用示例

    目录 线程池执行顺序 线程池配置策略 Spring线程池的配置类: Spring有.无返回值的异步调用示例 自定义的异步方法类代码: 测试类代码 常见问题: 参考文章: 线程池执行顺序 核心线程数(C ...

  4. async spring 默认线程池_SpringBoot中Async异步方法和定时任务介绍

    1.功能说明 Spring提供了Async注解来实现方法的异步调用.即当调用Async标识的方法时,调用线程不会等待被调用方法执行完成即返回继续执行以下操作,而被调用的方法则会启动一个独立线程来执行此 ...

  5. 线程池监控和动态配置

    线程池 线程池是一种 "池化" 的线程使用模式,通过创建一定数量的线程,让这些线程处于就绪状态来提高系统响应速度,在线程使用完成后归还到线程池来达到重复利用的目标,从而降低系统资源 ...

  6. java(线程池的创建方式,和线程池的原理)

    1.为什么要使用线程池:   减少资源消耗,通过重复的使用已创建好的线程,避免了线程的频繁创建和销毁所造成的消耗 提高响应速度,当任务到达的时候,不需要再去创建,可以直接使用已经创建好的线程就能立即执 ...

  7. Java多线程之线程池的参数和配置

    在Java多线程编程中,线程池是一种常见的技术,用于管理线程的创建和销毁.线程池中的线程可以被重复利用,从而减少了线程的创建和销毁的开销,提高了程序的性能.在Java中,线程池的参数和配置非常重要,不 ...

  8. 线程池的创建及参数设置详解

    一. 常见线程池 线程池的创建方法主要有两类,第一是通过Executors 创建线程池,第二是通过 ThreadPoolExecutor 创建线程池. 首先我们来看通过Executors 创建的线程池 ...

  9. 灵魂发问!线程池到底创建多少线程比较合理?

    虽然线程池的模型被剖析的非常清晰,但是如何最高性能地使用线程池一直是一个令人纠结的问题,其中最主要的问题就是如何决定线程池的大小.这篇文章会以量化测试的方式分析:何种情况线程池应该使用多少线程数. 计 ...

最新文章

  1. 汇编语言随笔(11)- int 指令(返回到应用程序的中断例程),实验13(编写、应用中断例程)
  2. 2019暑假第三周总结
  3. 三星I9000挖煤模式和R模式进入方法
  4. 综合学生信息管理系统(JSP+JDBC)
  5. mysql drop 权限_MySQL procedure drop权限问题
  6. 测试每个阶段需要输出的文档
  7. Java基础语法之变量作用域、小大驼峰命名规则和java运算符(赋值、关系、三元、逻辑运算符)
  8. 搭建SSM框架的配置文件
  9. Ren获得Zcash技术咨询委员会资助,将在币安智能链上启动RenZEC流动性引导计划
  10. 微信jssdk配置的问题,使用MVC制作的demo
  11. LeetCode(26): 删除排序数组中的重复项
  12. Soul Api 网关发布 1.0.3-RELEASE 版本
  13. 文本匹配工具正式开源
  14. 做一个和瑞幸咖啡一样的小程序,就能3月赚24亿了吗?
  15. 涛涛的若依学习笔记——登录
  16. 口袋的天空(Kruscal)
  17. matlab 相位解旋绕,相位解缠绕方法-南京航空航天大学学报.PDF
  18. 计算机加内存还是固态硬盘,电脑慢加内存还是固态硬盘好
  19. RGB_打开/获取颜色选择器(RGB)的几种方式/本地RGB查询/css颜色名参考
  20. 一个人,长相越来越年轻,是因为这三个习惯

热门文章

  1. 打印不同对象的字节表示 ( 对int*强制转换成unsigned char*的理解 )
  2. case when里的like功能 ////// 截取(substr)
  3. Mysql与Oracle区别
  4. Linux使用netstat命令查看并发连接数
  5. MVC缓存OutPutCache学习笔记 (一) 参数配置
  6. JPA相关--Annotation
  7. 参考答案:03 向量空间
  8. opencv3 视频稳像
  9. 广东电大计算机绘图试题,电大计算机绘图期末复习试题及答案参考小抄.doc
  10. 如何利用 Python 爬取 LOL 高清精美壁纸?