文章目录
Springboot使用异步线程池多线程处理耗时任务方法总结
- 本文基于springboot,总结了使用异步线程池处理耗时任务的方法
一、不使用自定义的线程池(简单的异步调用)
1.1 最简单的异步调用,不返回值
import java.util.concurrent.TimeUnit
@Component
public class AsyncDemo {
/**
* 最简单的异步调用,不返回值
* @Param sleepSecond
* @Return void
*/
@Async
public void asyncVoidTest(Integer sleepSecond) {
TimeUnit timeUnit = TimeUnit.SECONDS;
timeUnit.sleep(sleepSecond);
System.our.println("This is the async void test");
}
}
1.2 异步调用返回Future类
- 在并发编程中,我们经常用到非阻塞的模型,通过实现Callback接口,并用Future可以来接收多线程的执行结果,使用示例如下:
import java.util.concurrent.TimeUnit
import java.util.concurrent.Future;
@Component
public class AsyncDemo {
/**
* 异步调用返回Future类
* @param i
* @return
*/
@Async
public Future<String> asyncFutureTest(int i) {
Future<String> future;
try {
Thread.sleep(1000 * 1);
future = new AsyncResult<String>("success:" + i);
} catch (InterruptedException e) {
future = new AsyncResult<String>("error");
}
return future;
}
}
1.3 设置定时任务
- 我们还可以通过@Scheduled注解来进行异步定时任务的设置,可以通过设置fixedDelay、initialDelay等参数来使用,也可以通过cron表达式来进行使用
- cron表达式的使用方法可以参考我的博文Cron表达式详细解析
- 使用示例如下:
import java.util.concurrent.TimeUnit
@Component
public class AsyncDemo {
/**
* 不使用cron表达式的定时器设置方法
* initialDelay表示容器启动初始化后延迟5秒后执行一次定时器,fixedDelay表示每10秒执行一次定时器
* 如果想一开始就直接执行定时器的话,不设置initialDelay即可
* fixedDelay和initialDelay的单位都是1000表示1秒
* @Param sleepSecond
* @Return void
*/
@Async
@Scheduled(fixedDelay=1000 * 10, initialDelay=1000 * 5)
public void asyncNoCronTimerTest(Integer sleepSecond) {
TimeUnit timeUnit = TimeUnit.SECONDS;
timeUnit.sleep(sleepSecond);
System.our.println("This is the async no cron timer test");
}
/**
* 使用cron表达式的定时器设置方法
* @Param sleepSecond
* @Return void
*/
@Async
@Scheduled(cron = "* * * * * ?")
public void asyncCronTimerTest(Integer sleepSecond) {
TimeUnit timeUnit = TimeUnit.SECONDS;
timeUnit.sleep(sleepSecond);
System.our.println("This is the async cron timer test");
}
}
二、使用自定义的线程池(推荐使用此方法)
2.1 添加自定义线程池配置类
因为Springboot框架默认来一个请求开启一个线程,在高并发下容易内存溢出
所以使用时最好通过配置自定义线程池来进行处理异步任务
- 新建一个配置类,例如建一个ThreadPoolConfig.java,代码如下:
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 ThreadPoolConfig {
// @Bean里边的name可自定义成想要的内容
@Bean("MyThreadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 线程池创建的核心线程数,线程池维护现线程的最小数量,即使没有任务需要执行,也会一直存活
executor.setCorePoolSize(16);
// 如果设置allowCoreThreadTimeOut=true(默认false)时,核心线程会超时关闭
// executor.setAllowCoreThreadTimeout(true)
// 阻塞队列,当核心线程达到最大时,新任务会放在队列中排队等待执行
executor.setQueueCapacity(124);
// 最大线程池数量,当线程数>=corePoolSize,且任务队列已满时,线程池会创建新线程来处理任务
// 任务队列已满时,且当线程数=maxPoolSize,线程池会拒绝处理任务而抛出异常
executor.setMaxPoolSize(64);
// 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数=corePoolSize
// 允许线程空闲时间30秒,当maxPoolSize的线程在空闲时间到达的时候销毁
// 如果allowCoreThreadTimeOut=true,则会直到线程数量=0
executor.setKeepAliveSeconds(30);
// spring 提供的 ThreadPoolTaskExecutor 线程池是有setThreadNamePrefix()方法的
// jdk 提供的 ThreadPoolTaskExecutor 线程池是没有setThreadNamePrefix()方法的
executor.setThreadNamePrefix("目标特性提取系统线程池");
// rejection-policy:拒绝策略, 当线程数已经达到maxSize时候,如何处理新任务
// CallerRunsPolicy():交由调用方线程运行,比如main线程,如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行
// AbortPolicy():该策略时线程池的默认策略,如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException
// DiscardPolicy():如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常
// DiscardOldestPolicy():丢弃队列中最老的任务,队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
executor.initialize();
return executor;
}
}
2.2 使用自定义线程池
- 配置类添加好后,在使用@Async时直接把ThreadPoolConfig里边的Bean的name直接添加即可正常使用我们自定义的线程池了,使用示例如下:
~~~java
import java.util.concurrent.TimeUnit
@Component
public class AsyncDemo {
/**
* 使用自定义线程池
* @Param sleepSecond
* @Return void
*/
@Async("MyThreadPoolTaskExecutor")
public void asyncMyThreadPoolTest(Integer sleepSecond) {
TimeUnit timeUnit = TimeUnit.SECONDS;
timeUnit.sleep(sleepSecond);
System.our.println("This is the async my thread pool test");
}
}
以上就是Springboot使用异步线程池处理异步任务的方法,我感觉还是相对比较简单的,谢谢愿意收看我的博文的朋友,下期见