在 SpringBoot 应用中,经常会遇到在一个接口中,同时做事情1,事情2,事情3,如果同步执行的话,则本次接口时间取决于事情1 2 3执行时间之和;如果三件事同时执行,则本次接口时间取决于事情1 2 3执行时间最长的那个,合理使用多线程,可以大大缩短接口时间
快速使用
SpringBoot应用中需要添加@EnableAsync注解,来开启异步调用,一般还会配置一个线程池,异步的方法交给特定的线程池完成,如下:
@Slf4j
@Configuration
@EnableAsync
public class ThreadPoolCachedConfig {
/**
* 允许线程空闲时间(单位:默认为秒)
*/
private static final int KEEP_ALIVE_TIME = 10;
/**
* 缓冲队列数
*/
private static final int QUEUE_CAPACITY = 200;
/**
* 最大线程数
*/
private static final int maximnum_pool_size = 200;
private static final int THREADS = Runtime.getRuntime().availableProcessors() + 1;
private final ThreadFactory threadFactory = new ThreadFactoryBuilder()
// -%d不要少
.setNameFormat("tool-task-name-%d")
.setDaemon(true)
.build();
/**
* 自定义线程池
* corePoolSize 核心池的大小
* maximumPoolSize 线程池最大线程数
* keepAliveTime 表示线程没有任务执行时最多保持多久时间会终止
* unit 参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性
* TimeUnit.DAYS; //天
* TimeUnit.HOURS; //小时
* TimeUnit.MINUTES; //分钟
* TimeUnit.SECONDS; //秒
* TimeUnit.MILLISECONDS; //毫秒
* TimeUnit.MICROSECONDS; //微妙
* TimeUnit.NANOSECONDS; //纳秒
* workQueue 一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要
* threadFactory 用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程做些更有意义的事情
* handler 表示当拒绝处理任务时的策略,有以下四种取值
* @return
*/
@Bean("taskExecutor")
public Executor taskExecutor() {
log.info("ThreadPoolCachedConfig.class 自定义线程池执行成功。");
return new ThreadPoolExecutor(THREADS, 2 * THREADS,
KEEP_ALIVE_TIME, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(QUEUE_CAPACITY),
threadFactory, (r, executor) -> {
// 打印日志,添加监控等
log.warn("task is rejected!");
});
}
}
使用的方式非常简单,在需要异步的方法上加@Async注解。
获取异步方法返回值
当异步方法有返回值时,如何获取异步方法执行的返回结果呢?这时需要异步调用的方法带有返回值CompletableFuture。
CompletableFuture是对Feature的增强,Feature只能处理简单的异步任务,而CompletableFuture可以将多个异步任务进行复杂的组合。如下:
/**
* 异步线程任务
*
* @author wang gang
* @since 2021-11-09 11:11
*/
@Slf4j
@Service
public class AsyncServiceImpl {
@Value("${bigData.rainfall.prediction.url}")
private String bigDataRainfallPredictionUrl;
@Async("taskExecutor")
public CompletableFuture<List<BigDataRainfallPredicionPageVO>> bigDateRainfall(BigDataRainfallPredictionQO bigDataRainfallPredictionQO) {
try{
log.info("访问地址{}", bigDataRainfallPredictionUrl);
List<BigDataRainfallPredicionPageVO> bigDataRainfallPredicionPageVOList = obtainBigDataRainfallPredicionPageVOList(
bigDataRainfallPredictionQO, null);
return CompletableFuture.completedFuture(bigDataRainfallPredicionPageVOList);
}catch (Exception e){
log.error(e.toString());
return CompletableFuture.completedFuture(Collections.emptyList());
}
}
}
private List<BigDataRainfallPredicionPageVO> getBigDataRainfallPredicionPageVOList(LocalDateTime time){
LocalDate thisDate = time.toLocalDate();
LocalDateTime time2HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 01:00:00");
LocalDateTime time2HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 02:00:00");
//5点整
LocalDateTime time5HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 04:00:00");
LocalDateTime time5HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 05:00:00");
//8点整
LocalDateTime time8HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 07:00:00");
LocalDateTime time8HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 08:00:00");
//11点整
LocalDateTime time11HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 10:00:00");
LocalDateTime time11HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 11:00:00");
//14点整
LocalDateTime time14HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 13:00:00");
LocalDateTime time14HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 14:00:00");
//17点整
LocalDateTime time17HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 16:00:00");
LocalDateTime time17HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 17:00:00");
//20点整
LocalDateTime time20HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 19:00:00");
LocalDateTime time20HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 20:00:00");
//23点整
LocalDateTime time23HourFront = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 22:00:00");
LocalDateTime time23HourAfter = DateUtil.stringToLocalDateTime(DateUtil.localDateToString(thisDate) + " 23:00:00");
CompletableFuture<List<BigDataRainfallPredicionPageVO>> task2Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time2HourFront, time2HourAfter));
CompletableFuture<List<BigDataRainfallPredicionPageVO>> task5Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time5HourFront, time5HourAfter));
CompletableFuture<List<BigDataRainfallPredicionPageVO>> task8Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time8HourFront, time8HourAfter));
CompletableFuture<List<BigDataRainfallPredicionPageVO>> task11Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time11HourFront, time11HourAfter));
CompletableFuture<List<BigDataRainfallPredicionPageVO>> task14Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time14HourFront, time14HourAfter));
CompletableFuture<List<BigDataRainfallPredicionPageVO>> task17Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time17HourFront, time17HourAfter));
CompletableFuture<List<BigDataRainfallPredicionPageVO>> task20Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time20HourFront, time20HourAfter));
CompletableFuture<List<BigDataRainfallPredicionPageVO>> task23Hour = asyncService.bigDateRainfall(builderBigDataRainfallPredictionQO(time23HourFront, time23HourAfter));
// 等待所有任务都执行完
CompletableFuture.allOf(task2Hour, task5Hour, task8Hour, task11Hour, task14Hour, task17Hour, task20Hour, task23Hour).join();
List<BigDataRainfallPredicionPageVO> bigDataRainfallPredicionPageList = Lists.newArrayList();
try {
// 获取每个任务的返回结果
bigDataRainfallPredicionPageList.addAll(task2Hour.get());
bigDataRainfallPredicionPageList.addAll(task5Hour.get());
bigDataRainfallPredicionPageList.addAll(task8Hour.get());
bigDataRainfallPredicionPageList.addAll(task11Hour.get());
bigDataRainfallPredicionPageList.addAll(task14Hour.get());
bigDataRainfallPredicionPageList.addAll(task17Hour.get());
bigDataRainfallPredicionPageList.addAll(task20Hour.get());
bigDataRainfallPredicionPageList.addAll(task23Hour.get());
} catch (Exception e) {
log.error(e.toString());
}
return bigDataRainfallPredicionPageList;
}
https://zhuanlan.zhihu.com/p/134636915 SpringBoot中如何优雅的使用多线程