目录

线程池执行顺序

线程池配置策略

Spring线程池的配置类:

Spring有、无返回值的异步调用示例

自定义的异步方法类代码:

测试类代码

常见问题:

参考文章:


线程池执行顺序

核心线程数(CorePoolSize) ---> 全部被占用 ----> 剩余线程任务放入阻塞队列 ----> 队列全部被占用 ----> 根据最大线程数(MaxPoolSize)扩充线程数量 ----> 全部线程和队列都被占用 ----> 执行拒绝策略(舍弃并报错,舍弃但不报错,丢给调用方的线程执行等。。)

线程池配置策略

首先要了解自己本机或服务器的物理核心数,线程数设置太大会导致线程不停地切换,并不是真正意义上的并行。

(1)高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换,可以将阻塞队列设置大一点,最大线程数与核心线程数相当,这样可以避免创建太多的线程。 (2)并发不高、任务执行时间长的业务要区分开看: a、假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务 b、假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和 (1) 一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换

Spring线程池的配置类:

举例了两种不同的线程池策略,在注入时需要标明bean的name属性,即可调用具体的线程池。

注:不同业务不要混用同一种线程池策略,应该单独配置适合业务的配置,

同一台服务器中的微服务的线程池总数不能太多,

最大线程数必须大于等于核心线程数,否则会报错。

package com.example.jucdemo.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.ThreadPoolExecutor;@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {// 低并发,IO耗时长的策略@Bean(name = "myExecutor")public ThreadPoolTaskExecutor taskExecutor(){ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 较大核心线程数executor.setCorePoolSize(20);executor.setMaxPoolSize(100);executor.setKeepAliveSeconds(60);executor.setQueueCapacity(30);// 设置线程池内线程名称的前缀executor.setThreadNamePrefix("IO-threadPool-");//设置任务的拒绝策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//初始化executor.initialize();return executor;}// 高并发,耗时短的策略@Bean(name = "concurrencyExecutor")public ThreadPoolTaskExecutor concurrencyExecutor(){ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 小核心线程数executor.setCorePoolSize(5);executor.setMaxPoolSize(5);executor.setKeepAliveSeconds(2);// 大阻塞队列executor.setQueueCapacity(500);// 设置线程池内线程名称的前缀executor.setThreadNamePrefix("concurrency-threadPool-");// 设置任务的拒绝策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());// 初始化executor.initialize();return executor;}
}

Spring有、无返回值的异步调用示例

无返回值的较为简单,直接把耗时的任务设置为异步方法,丢给线程池执行。

有返回值的需要异步方法返回的是 Future<> 接口,

一般return new AsyncResult<>(result),将真正的结果传入。AsyncResult实现了ListenableFuture接口,而后者又继承了Future接口。

在需要其结果时调用 future.get()方法即可将结果取出,get()方法会阻塞当前线程,等待异步方法的返回值。

直接用 threadPoolTaskExecutor.excute(Runnable task)或.submit(Runnable task)来执行无返回值的异步方法。

用 .submit(Callable task) 方法执行有返回值的异步方法。这里的Runnable和Callable都是函数式接口,只有一个抽象方法,因此建议使用lamda表达式写法更为简洁。

具体见下面的代码示例。

自定义的异步方法类代码:

其中的 @Async 注解可在类或方法上标注,分别用来标记一个类下的所有方法为异步方法,或标记一个具体的方法为异步方法,可传入具体使用的线程池的bean的名字。

package com.example.jucdemo.service;import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;@Service
public class MyAsyncTest { // 无返回值@Async("myExecutor")public void execute(){System.out.println("开始执行异步任务--1");try {for (int i=0;i<10;i++){System.out.println("异步任务--1:第"+(i+1)+"次");Thread.sleep(100);}} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("异步任务--1:最终完成");}// 有返回值@Async("myExecutor")public Future<Integer> algorithm1(Integer var1,Integer var2) throws InterruptedException {Thread.sleep(1000);return new AsyncResult<>(var1+var2);}@Async("myExecutor")public Future<Integer> algorithm2(Integer var1,Integer var2) throws InterruptedException {Thread.sleep(2000);return new AsyncResult<>(var1 * var2);}@Async("myExecutor")public Future<String> circulation(int i) throws InterruptedException {Thread.sleep(500);return new AsyncResult<>(String.format("结果一:%s",i));}}

测试类代码

package com.example.jucdemo;import com.example.jucdemo.service.MyAsyncTest;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;@SpringBootTest
class JucDemoApplicationTests {@Resource(name = "myExecutor")ThreadPoolTaskExecutor executor;@ResourceMyAsyncTest asyncTest;// 无返回值的异步调用@Testpublic void executeTest() throws InterruptedException {System.out.println("主线程开始执行");// 方式一:调用 MyAsyncTest 类中定义的异步方法asyncTest.execute();// 方式二:直接调用线程池的execute方法executor.execute(() -> {System.out.println("开始执行异步任务--2");for (int i=0;i<10;i++){try {System.out.println("异步任务--2:第"+(i+1)+"次");Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("异步任务--2:最终完成");});//由于单元测试主线程结束进程直接结束,所以主线程需要sleep等待异步方法Thread.sleep(2000);System.out.println("进程结束");}// 带返回值的异步调用@Testpublic void submitTest() throws ExecutionException, InterruptedException {long start = System.currentTimeMillis();// 调用异步算法1Future<Integer> param1 = executor.submit(() -> {System.out.println("正在计算param1");Thread.sleep(3000);return 1;});// 调用异步算法2Future<Integer> param2 = executor.submit(() -> {System.out.println("正在计算param2");Thread.sleep(5000);return 2;});// 等待接收异步算法的结果int result = param1.get() + param2.get();long end = System.currentTimeMillis();long time = end - start;System.out.println("总共耗时:" + time);System.out.println("计算结果为:" + result);}// 带返回值的异步方法的调用@Testpublic void asyncResultTest() throws InterruptedException, ExecutionException {long start = System.currentTimeMillis();System.out.println("开始:");int var1 = 2;int var2 = 3;Future<Integer> result1 = asyncTest.algorithm1(var1, var2);Future<Integer> result2 = asyncTest.algorithm2(var1, var2);System.out.println("结果二" + result2.get());long end1 = System.currentTimeMillis();System.out.println("结果一:" + result1.get());long end2 = System.currentTimeMillis();System.out.println("时间一:" + (end1 - start));System.out.println("时间二:" + (end2  - start));}@Testpublic void circulationTest() throws InterruptedException, ExecutionException {long start = System.currentTimeMillis();List<Future<String>> futures = new ArrayList<>();List<String> resultList = new ArrayList<>();//批量调用异步方法for (int i=1;i<=10;i++){Future<String> f = asyncTest.circulation(i);futures.add(f);}//全部执行完毕后统一获取for (Future<String> future : futures) {resultList.add(future.get());}long end = System.currentTimeMillis();System.out.println("结果为:" + resultList);System.out.println("耗时:" + (end - start));}}

常见问题:

以下会使@Async注解失效

一、异步方法不能使用static修饰

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

三、@Async标注的异步方法与调用其的方法不能在同一个类中

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

五、使用SpringBoot框架必须在启动类或配置类中增加@EnableAsync注解

六、在Async 方法上标注@Transactional是没用的。 在Async 方法调用的方法上标注@Transactional 有效

参考文章:

线程池的各种使用场景_星空dream的博客-CSDN博客_线程池的应用场景

@EnableAsync@Async使用总结 - 在贝加尔湖畔 - 博客园 (cnblogs.com)

SpringBoot异步任务, 以及带返回值的异步任务(@Async 不起作用的原因)_晴天小哥哥的博客-CSDN博客_springboot方法异步

SpringBoot线程池ThreadPoolTaskExecutor和@Async异步方法浅解及代码应用示例相关推荐

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

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

  2. SpringBoot 线程池,也太好用了叭!

    欢迎关注方志朋的博客,回复"666"获面试宝典 来源:blog.csdn.net/m0_37701381/article/details/81072774 前言 前两天做项目的时候 ...

  3. SpringBoot线程池的创建、@Async配置步骤及注意事项

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:Muscleheng blog.csdn.net/Muscl ...

  4. springboot自带的线程池ThreadPoolTaskExecutor、ThreadPoolTaskScheduler的深入应用——异步任务监听回调,任务中断案例

    一.常用的的线程池对象 1.jdk原生的两个常用线程池对象 ThreadPoolExecutor.ScheduledThreadPoolExecutor,后者继承前者,主要增加了任务调度相关的一些方法 ...

  5. Springboot线程池的使用和扩展

    我们常用ThreadPoolExecutor提供的线程池服务,springboot框架提供了@Async注解,帮助我们更方便的将业务逻辑提交到线程池中异步执行,今天我们就来实战体验这个线程池服务: 实 ...

  6. springboot线程池配置

    1. application.yml配置 # 异步线程配置 # 配置核心线程数 async.executor.thread.core_pool_size: 10 # 配置最大线程数 async.exe ...

  7. springboot 线程池_Spring boot 2 线程池怎么配置

    线程池 在java 中线程池,就是ThreadPoolExecutor来构造,简单看下线程池包含的方法, corePoolSize:初始化线程.线程池中保留的线程数量. maximumPoolSize ...

  8. 一文详解java线程池 详解Java线程池的七个参数 详解池化技术 java如何选择核心线程数 详解Java线程池的拒绝策略

    目录 引言 线程池使用场景 加快请求响应(响应时间优先) 加快处理大任务(吞吐量优先) 特殊说明 线程池的池化技术 线程池的创建 手动创建 创建newFixedThreadPool线程池 创建newS ...

  9. Java 线程池及参数动态调节详解

    前前言:本文搬自:why技术 前言:曾经自诩对线程池了如指掌,不料看了美团的一篇技术文章后才知道原来线程池的参数还可以动态调节. 经典面试题 在这篇文章中我主要回答上面抛出的这个问题:你这几个参数的值 ...

最新文章

  1. 机器学习PAL数据可视化
  2. Qt导入CMakeLists.txt后无法调试
  3. sqlite--代码操作
  4. 【转】NHIBERNATE的各种保存方式的区别 (SAVE,PERSIST,UPDATE,SAVEORUPDTE,MERGE,FLUSH,LOCK)
  5. myclipes 配置php,myclipse使用技巧
  6. 服务器 ha linux,Linux 高可用(HA)集群之Heartbeat详解
  7. android jni 调用java对象_Android NDK开发之Jni调用Java对象
  8. 【Linux入门学习之】grep命令详解
  9. DMA流程简介--CPU/内存/网卡之间的交互
  10. .NET中的加密类(对称加密)
  11. jQuery使table表格隔行显示不同颜色
  12. PHP实现敏感词过滤
  13. 阿里矢量图标库的使用方法
  14. 易语言 火眼 哈勃 防分析源码
  15. CNAS仪器校准人员需要遵守哪些规范?
  16. 学前端是去培训班还是自学好?
  17. eval函数和repr函数
  18. echarts正负极柱状图
  19. Basler Blaze-101开发实践(1)——实时采图
  20. 颁奖 | 获奖名单又来惹~!有你咩?

热门文章

  1. 基于PHP+MySQL米步童鞋商城网站的设计与实现
  2. Steam教育在人文研究领域体现的综合素养
  3. 盗版导致印度成为网络犯罪重灾区
  4. 【小沐学NLP】Python实现聊天机器人(Selenium、七嘴八舌)
  5. 年末放大招,Java进阶大数据3W全套视频免费领!
  6. JZ高中OJ 1420.佳肴
  7. mongodb中的in和notin的查询
  8. Threadx tx_thread_create创建线程
  9. Linux PCI网卡驱动的详细分析
  10. 戴森史上最轻吸尘器全球首发,搭配吸头后仅重1.5千克