定时任务加线程池处理任务的几种方式

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秒模拟逻辑处理需要的耗时

运行结果:

image.png

通过运行结果可以看到,每次任务执行都间隔了六秒,即定时任务执行的时候并不是按照设置的轮转时间来执行,而是需要等待任务执行结束后再进行下一次的轮转;也就是说上一个定时任务没有执行完成的话,下一个任务将顺延;

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秒,最后关闭线程池;

运行结果:

image.png
image.png

通过运行结果可以看到,任务执行的间隔时间都是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中,最后判断返回结果个数和线程个数是否一致,如不一致可以进行重试操作!

执行结果:

image.png

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值