SpringBoot2后端项目-定时任务例子

目录

1、基于Timer的定时任务

1.1介绍

1.2例子

1.3 前端请求

 1.4 Controller 层

1.5 Service 层

 1.6 结果

2、基于ScheduledExecutorService 的定时任务

2.1介绍

2.2 例子

2.3 Service 层

2.4 线程异常打印日志

2.5 运行结果

3、改进

3.1 线程池配置类

3.2 异步任务管理器

3.3 Service 层

3.4 运行结果


1、基于Timer的定时任务

1.1介绍

在JDK1.5之前使用的都是 Timer来完成定时任务, 特点是 单线程执行定时任务,因此存在以下问题:

  • 多任务之间相互影响(只要有一个没有捕获的异常出现,其它任务便会自动终止运行)
  • 多任务的执行是串行的,效率低

1.2例子

使用 PostMan 发送POST 请求:localhost:8888/ruoyi/login ,请求参数: 用户名、密码、验证码、uuid

Controller 层接收请求,并调用 service 层处理请求

sercie 层启动两个定时任务

一个任务是 延迟3秒打印 用户名、密码、验证码和uuid 和线程名

另一个任务是 延迟1秒,每间隔1秒打印 当前时间 和线程名

1.3 前端请求

 1.4 Controller 层

package com.ruoyi.project.system.controller;
/**
 * 登录验证
 *
 * @author ruoyi
 */
@RestController
public class SysLoginController {

    @Resource
    private SysLoginService loginService;

    @PostMapping("/login")
    public AjaxResult login(String username, String password, String code, String uuid) {
        AjaxResult ajax = AjaxResult.success();
        // 生成令牌
        String token = loginService.login(username, password, code, uuid);
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }

}

1.5 Service 层

 1.6 结果

2、基于ScheduledExecutorService 的定时任务

2.1介绍

ScheduledExecutorService 是 JDK1.5 出现的,是基于多线程的,线程之间互不影响

2.2 例子

在 上一个例子中,我们发现 通过 timer 方式实现的定时器任务, 只要有一个任务出现异常而没有处理, 那么其他任务都会终止!!

本例子将解决这个问题。

2.3 Service 层

前端请求 和 Controller层代码都一样的 这里就不重复了

2.4 线程异常打印日志

package com.ruoyi.common.utils;
/**
 * 线程相关工具类.
 *
 * @author ruoyi
 */
public class Threads {

    private static final Logger logger = LoggerFactory.getLogger(Threads.class);


    /**
     * 打印线程异常信息
     */
    public static void printException(Runnable r, Throwable t) {
        if (t == null && r instanceof Future<?>) {
            try {
                Future<?> future = (Future<?>) r;
                if (future.isDone()) {
                    future.get();
                }
            } catch (CancellationException ce) {
                t = ce;
            } catch (ExecutionException ee) {
                t = ee.getCause();
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }
        if (t != null) {
            logger.error(t.getMessage(), t);
        }
    }

}

2.5 运行结果

3、改进

上一个例子中 ScheduledExecutorService 对象是 new 出来的,这样的编码方式不好!

①、ScheduledExecutorService 对象应该交由 Spring 容器进行管理。

②、应该对 ScheduledExecutorService 进行封装,做成一个 单例的异步任务管理器

3.1 线程池配置类

该配置类,向Spring容器中注入 ScheduledExecutorService 对象

package com.ruoyi.framework.config;
/**
 * 线程池配置
 *
 * @author ruoyi
 **/
@Configuration
public class ThreadPoolConfig {
    // 核心线程池大小
    private int corePoolSize = 50;

    /**
     * 执行周期性或定时任务
     */
    @Bean(name = "scheduledExecutorService")
    protected ScheduledExecutorService scheduledExecutorService() {
        return new ScheduledThreadPoolExecutor(corePoolSize,
                new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) {
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                super.afterExecute(r, t);
                Threads.printException(r, t);
            }
        };
    }

}

3.2 异步任务管理器

package com.ruoyi.framework.manager;
/**
 * 异步任务管理器
 *
 * @author ruoyi
 */
public class AsyncManager {

    /**
     * 操作延迟10毫秒
     */
    private final int OPERATE_DELAY_TIME = 10;

    /**
     * 异步操作任务调度线程池
     */
    private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");

    /**
     * 单例模式
     */
    private AsyncManager() {
    }

    private static AsyncManager me = new AsyncManager();

    public static AsyncManager me() {
        return me;
    }

    /**
     * 执行任务
     *
     * @param task 任务
     */
    public void execute(TimerTask task) {
        executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
    }

}


3.3 Service 层

前端和Controller层代码不变

3.4 运行结果

发布了98 篇原创文章 · 获赞 8 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/u010559460/article/details/104882540