定时任务加线程池处理任务的几种方式
1、场景:定时任务设置每秒执行一次,但是每个任务的逻辑处理耗时超过1秒,那么定时任务是按照每秒执行一次还是每个任务执行完成后再按设置的时间执行?
代码:
private static final String TIP = "定时任务->";
private static int TASK_ONE_NUM, TASK_TWO_NUM, TASK_THREE_NUM = 0;
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Scheduled(cron = "0/1 * * * * ?")
public void startThree(){
log.info(TIP + "单个任务,上一个定时任务未执行完成下一个顺延");
taskThree();
}
public void taskThree(){
log.info("第" + (++TASK_THREE_NUM) + "次执行定时任务,时间:" + format.format(new Date()));
int i = TASK_THREE_NUM;
try {
Thread.sleep(5000);
log.info("第" + i + "次任务逻辑!!!!!!!!!!!!");
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("第" + TASK_THREE_NUM + "定时任务结束,时间:" + format.format(new Date()));
}
代码中设置定时任务每秒执行一次,具体逻辑处理中让线程睡眠5秒模拟逻辑处理需要的耗时
运行结果:
通过运行结果可以看到,每次任务执行都间隔了六秒,即定时任务执行的时候并不是按照设置的轮转时间来执行,而是需要等待任务执行结束后再进行下一次的轮转;也就是说上一个定时任务没有执行完成的话,下一个任务将顺延;
2、场景:定时任务设置每秒执行一次,每个任务都开辟一个线程进行处理(这里使用线程池)那么定时任务是会按照设置的时间轮转执行还是将任务顺延?
代码:
private static final String TIP = "定时任务->";
private static int TASK_ONE_NUM, TASK_TWO_NUM, TASK_THREE_NUM = 0;
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Scheduled(cron = "0/1 * * * * ?")
public void startThree(){
log.info(TIP + "单个任务,上一个定时任务未执行完成下一个顺延");
taskTwo(5);
}
/**
* num = 线程池核心线程数 = 线程池最大线程数 = 任务数
*/
public void taskTwo(int num) {
log.info("第" + (++TASK_TWO_NUM) + "次执行定时任务,时间:" + format.format(new Date()));
int time = TASK_TWO_NUM;
//每次定时任务初始化一个线程池,定时任务结束后关闭 如果时间周期短可以全局创建但是任务执行完不能关闭
threadPool = new ThreadPoolExecutor(num,
num,
0,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>());
//构造一个数组用于请求线程
ArrayList<Object> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
list.add("1");
}
try {
//runAsync异步执行,无返回值
CompletableFuture[] cfs = list.stream().map(urlMap -> CompletableFuture.runAsync(() -> {
try {
log.info("================================= 异步线程" + Thread.currentThread().getName() + "开始执行");
Thread.sleep(3000);
log.info("================================= 异步线程" + Thread.currentThread().getName() + "执行结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, threadPool)).toArray(CompletableFuture[]::new);
} finally {
log.info("第" + time + "次定时任务,关闭线程池!!");
threadPool.shutdown();
}
}
代码中设置定时任务间隔1秒执行,每次执行时构造一个线程池,且线程池中的线程异步处理逻辑,每个处理逻辑的线程都休眠3秒,最后关闭线程池;
运行结果:
通过运行结果可以看到,任务执行的间隔时间都是1秒,直到第四次定时任务开始的时候线程池1中的五个线程才处理完成,即完全按照设置的时间间隔来执行任务,并不等待每次任务执行结束再进行下一次轮转;
3、场景:既要定时任务,又要多线程,每次任务没有处理完成的话下一次任务顺延,即等待上一个任务完成再执行下一次任务;
代码:
private static final String TIP = "定时任务->";
private static int TASK_ONE_NUM, TASK_TWO_NUM, TASK_THREE_NUM = 0;
private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Scheduled(cron = "0/1 * * * * ?")
public void startThree(){
log.info(TIP + "单个任务,上一个定时任务未执行完成下一个顺延");
taskOne(3);
}
/**
* num = 线程池核心线程数 = 线程池最大线程数 = 任务数
*/
public void taskOne(int num) {
log.info("第" + (++TASK_ONE_NUM) + "次执行定时任务,时间:" + format.format(new Date()));
//每次定时任务初始化一个线程池,定时任务结束后关闭 如果时间周期短可以全局创建但是任务执行完不能关闭
if (threadPool == null || threadPool.getPoolSize() != num) {
threadPool = new ThreadPoolExecutor(num,
num,
0,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>());
}
//构造一个数组用于请求线程
ArrayList<Object> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
list.add("1");
}
try {
List returnList = new ArrayList();
//supplyAsync方式执行需要有返回值,每个线程执行完将返回值插入returnList最后通过returnList和list的长度判断是否所有线程都执行结束
CompletableFuture[] cfs = list.stream().map(urlMap -> CompletableFuture.supplyAsync(() -> {
try {
log.info("================================= 异步线程" + Thread.currentThread().getName() + "开始执行");
Thread.sleep(3000);
log.info("================================= 异步线程" + Thread.currentThread().getName() + "执行结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}, threadPool)
.thenApply(h -> h)
.whenComplete((v, e) -> {
if (v) {
returnList.add("over");
}
})).toArray(CompletableFuture[]::new);
//等待线程全部执行完成
CompletableFuture.allOf(cfs).join();
while (returnList.size() == list.size()) {
log.info("打印所有线程执行结果:" + JSONArray.toJSONString(returnList));
break;
}
} finally {
log.info("第" + TASK_ONE_NUM + "次定时任务,关闭线程池!!!");
threadPool.shutdown();
}
}
对比第二种方式,使用CompletableFuture.allOf(cfs).join();等待线程池中的线程全部执行完成,即线程池内的任务处理还是异步,但是主方法执行到CompletableFuture.allOf(cfs).join()时将阻塞,等待线程池内所有线程处理结束,每个逻辑处理线程执行完成后都将返回一个over字符串并放入retuenList中,最后判断返回结果个数和线程个数是否一致,如不一致可以进行重试操作!
执行结果:
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战