spring 内置定时器学习

1.为什么需要?

由于项目中需要根据配置的参数来动态启用和暂停定时任务。

2.实战代码

实战一: 可以根据数据库时间动态改变但不能停止和启动


import com.xxxx.xxxx.service.WmgMlService;
import com.xxxx.xxxx.util.StringUtils;
import com.xxxx.xxxx.webSocket.XmjgJytsWebSocket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

/**
 * 建议投诉 ---定时任务
 * @author wyz
 * @create 2019-07-2914:26
 */
@Configuration
@EnableScheduling //开启定时任务
public class ScheduledConfig implements SchedulingConfigurer{

    public static Logger logger = LoggerFactory.getLogger(ScheduledConfig.class);

    @Autowired
    private WmgMlService wmgMlService;

    /**
     * 获取cron表达式
     *
     * @param type   配置信息类型
     * @param timeType    时间类型 AM:早上 PM:下午
     * @return
     */
    public String getCron(String type,String timeType) {
        String result = "";
        List<Map> list = wmgMlService.getXmjgCsConfig(type);
        if(null != list && list.size()>0){
            if(StringUtils.equal("AM",timeType)){
                String amStart = list.get(0).get("amTimeStart").toString();
                amStart = amStart.substring(amStart.indexOf(" "),amStart.indexOf(":")).trim();
                String amEnd = list.get(0).get("amTimeEnd").toString();
                amEnd = amEnd.substring(amEnd.indexOf(" "),amEnd.indexOf(":")).trim();
                result ="* * "+amStart+"-"+amEnd+" * * ?";
                return result;
            }else{
                String pmStart = list.get(0).get("pmTimeStart").toString();
                pmStart = pmStart.substring(pmStart.indexOf(" "),pmStart.indexOf(":")).trim();
                String pmEnd = list.get(0).get("pmTimeEnd").toString();
                pmEnd = pmEnd.substring(pmEnd.indexOf(" "),pmEnd.indexOf(":")).trim();
                result ="* * "+pmStart+"-"+pmEnd+" * * ?";
                return result;
            }
        }
        return result;
    }

    //定时任务
    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10);
        scheduledTaskRegistrar.setScheduler(executorService);
        scheduledTaskRegistrar.addTriggerTask(new MyTask("C_WAIT_HANDLE_AM"),new MyTrigger("C_WAIT_HANDLE","AM"));
        scheduledTaskRegistrar.addTriggerTask(new MyTask("C_WAIT_HANDLE_PM"),new MyTrigger("C_WAIT_HANDLE","PM"));
        scheduledTaskRegistrar.addTriggerTask(new MyTask("S_WAIT_HANDLE_AM"),new MyTrigger("S_WAIT_HANDLE","AM"));
        scheduledTaskRegistrar.addTriggerTask(new MyTask("S_WAIT_HANDLE_PM"),new MyTrigger("S_WAIT_HANDLE","PM"));
        scheduledTaskRegistrar.destroy();
    }

    //定义任务线程
    class MyTask implements Runnable{
        private String taskName;
        public MyTask(String name){
            this.taskName =name;
        }
        //业务逻辑
        @Override
        public void run() {
            System.out.println(taskName+"======>执行动态定时任务时间: " + LocalDateTime.now()+",线程名字:"+Thread.currentThread().getName());
            boolean result =  wmgMlService.isNewCsInfo(1,1);
            if(!result){//如果新增信息
                XmjgJytsWebSocket.sendInfo("有新增信息");
            }
            System.out.println("result====>"+result);
        }
    }
    //定义触发器
    class MyTrigger implements Trigger{
        private String type;
        private String timeType;
        public MyTrigger(String type,String timeType){
            this.type=type;
            this.timeType=timeType;
        }

        @Override
        public Date nextExecutionTime(TriggerContext triggerContext) {
            //每次调度时加载cron表达式
            String cron = getCron(type,timeType);
            System.out.println("cron====>"+cron);
            CronTrigger cronTrigger = new CronTrigger(cron);
            return cronTrigger.nextExecutionTime(triggerContext);
        }
    }


}

实战2:可以暂停并利用多线程实现多个线程启动可停止定时器


import com.xxxx.xxxx.service.WmgMlService;
import com.xxxx.xxxx.util.StringUtils;
import com.xxxx.xxxx.webSocket.XmjgJytsWebSocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

/**
 * 利用线程池实现任务调度
 * Task任务调度器可以实现任务的调度和删除
 * 原理:
 * 实现一个类:ThreadPoolTaskScheduler线程池任务调度器,能够开启线程池进行任务调度
 * ThreadPoolTaskScheduler.schedule()方法会创建一个定时计划ScheduleFuture,
 * 在这个方法中添加两个参数一个是Runable:线程接口类,和CronTrigger(定时任务触发器)
 * 在ScheduleFuture中有一个cancel可以停止定时任务
 * <p>
 * Scheduled Task是一种轻量级的任务定时调度器,相比于Quartz,减少了很多的配置信息,但是Scheduled Task
 * 不适用于服务器集群,引文在服务器集群下会出现任务被多次调度执行的情况,因为集群的节点之间是不会共享任务信息的
 * 每个节点的定时任务都会定时执行
 * <p>
 * 建议投诉-[逾期提醒]和[新增提醒]和[配置提醒时间]功能实现逻辑:
 * 1.用户访问建议投诉页面时,触发后台开始定时任务
 * 2.用户离开页面时,触发后台停止定时任务
 * 3.用户在页面修改参数配置后,触发后台修改定时任务。
 */
@RestController
@EnableScheduling
public class XmjgJytsTaskSchedule {
    @Autowired
    private WmgMlService wmgMlService;

    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    private Map<String, ScheduledFuture<?>> map = new ConcurrentHashMap(); //记录任务

    private int csTotal;//投诉建议总数量--作为比较中介 定义为类级全局变量
    private int csTotalStop = 0;//停止定时任务是保存的投诉建议总数量

    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
        threadPoolTaskScheduler.setPoolSize(20);//设置线程池大小
        return threadPoolTaskScheduler;
    }
    /**
     * 1,定义一个方法实现定时任务的启动
     * 2,定义一个方法实现用于终止定时任务
     * 3,修改定时任务时间:changeCron
     */
    /**
     * 启动定时器
     *
     * @return
     */
    @RequestMapping("/startTask")
    @ResponseBody
    public String StartTask(Map<String, CronTrigger> mapMyTask) {
        /**
         * task:定时任务要执行的方法
         * trigger:定时任务执行的时间
         */
        endTask();//先停止所有 防止恶意在无限开始 Map过大引发内存溢出
        mapMyTask = initMyTask();//初始化Map
        if (csTotalStop == 0) {
            csTotal = wmgMlService.getCsTotal();
        } else {
            csTotal = csTotalStop;
        }

        map.put("C_WAIT_HANDLE_AM", threadPoolTaskScheduler.schedule(new MyOverdueInfoTask("[投诉逾期上午提醒定时器]"), mapMyTask.get("C_WAIT_HANDLE_AM")));
        map.put("C_WAIT_HANDLE_PM", threadPoolTaskScheduler.schedule(new MyOverdueInfoTask("[投诉逾期下午提醒定时器]"), mapMyTask.get("C_WAIT_HANDLE_PM")));
        map.put("S_WAIT_HANDLE_AM", threadPoolTaskScheduler.schedule(new MyHaveNewInfoTask("[建议未处理上午提醒定时器]"), mapMyTask.get("S_WAIT_HANDLE_AM")));
        map.put("S_WAIT_HANDLE_PM", threadPoolTaskScheduler.schedule(new MyHaveNewInfoTask("[建议未处理下午提醒定时器]"), mapMyTask.get("S_WAIT_HANDLE_PM")));
        System.out.println("建议投诉监听新消息和逾期提醒开始了!!!时间:" + LocalDateTime.now());
        return "startTask";
    }

    /**
     * 停止定时任务
     *
     * @return
     */
    @RequestMapping("/endTask")
    @ResponseBody
    public String endTask() {
        Iterator<String> iterator = map.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            if (map.get(key) != null) {
                map.get(key).cancel(true);//停止定时任务
            }
            map.remove(key);
            System.out.println(key + "======>定时任务停止,时间:" + LocalDateTime.now());
        }
        //保存此次暂停的总记录数
        csTotalStop = csTotal;
        System.out.println("暂停的总记录数为:"+csTotalStop);
        return "endTask";
    }

    /**
     * 改变调度的时间
     * 步骤:
     * 1,先停止定时器
     * 2,在启动定时器
     */
//    @RequestMapping("/changeTask")
//    @ResponseBody
    public String changeTask() {
        //停止定时器
        endTask();
        //定义新的执行时间
        Map<String, CronTrigger> mapMyTask = initMyTask();//初始化触发器
        //启动定时器
        StartTask(mapMyTask);
        System.out.println("建议投诉监听新消息和逾期提醒修改并启动了!!!时间:" + LocalDateTime.now());
        return "changeTask";
    }

    //定义任务线程--新增信息提醒功能
    class MyHaveNewInfoTask implements Runnable {
        private String taskName;

        public MyHaveNewInfoTask(String name) {
            this.taskName = name;
        }

        //业务逻辑
        @Override
        public void run() {
            System.out.println(taskName + "======>定时任务开始,时间: " + LocalDateTime.now() + ",线程名字:" + Thread.currentThread().getName());
            int lastData = wmgMlService.getCsTotal(); //数据库最新的建议投诉总数
            int insertSum = 0; //新增的个数
            boolean result = false;
            if (lastData > csTotal) {//新增数据
                insertSum = lastData - csTotal;
                csTotal = lastData;
                result = true;
            } else if (lastData < csTotal) {//删除数据
                csTotal = lastData;
            }
            if (result) {
                XmjgJytsWebSocket.sendInfo("您有" + insertSum + "条新信息!");//新增信息提醒
            }
            System.out.println("result====>" + result);
        }
    }

    //定义任务线程--逾期信息提醒功能
    class MyOverdueInfoTask implements Runnable {
        private String taskName;

        public MyOverdueInfoTask(String name) {
            this.taskName = name;
        }

        //业务逻辑
        @Override
        public void run() {
            System.out.println(taskName + "======>定时任务开始,时间: " + LocalDateTime.now() + ",线程名字:" + Thread.currentThread().getName());
            List<Map> result = wmgMlService.getOverTimeInfoTotal();
            if (null != result && result.size() > 0) {
                String one = result.get(0).get("overTimeTwoTotal").toString();//逾期2天个数
                String two = result.get(0).get("overTimeSevenTotal").toString();//逾期2天个数
                if (!StringUtils.equal("0", one) && !StringUtils.equal("0", two)) {
                    XmjgJytsWebSocket.sendInfo("未处理的投诉已经超过2天,请及时处理。\n" +
                            "未处理的投诉已经超过7天,请及时处理");//逾期信息提醒
                } else if (!StringUtils.equal("0", one) || StringUtils.equal("0", two)) {
                    XmjgJytsWebSocket.sendInfo("未处理的投诉已经超过2天,请及时处理。");//逾期信息提醒
                } else if (!StringUtils.equal("0", two) || StringUtils.equal("0", one)) {
                    XmjgJytsWebSocket.sendInfo("未处理的投诉已经超过7天,请及时处理。");//逾期信息提醒
                }
            }
        }
    }

    /**
     * 获取cron表达式
     *
     * @param type     配置信息类型
     * @param timeType 时间类型 AM:早上 PM:下午
     * @param warnType 提醒类型 1为逾期提醒 2为新增提醒
     * @return
     */
    public String getCron(String type, String timeType, String warnType) {
        String result = "";
        String warnTimeStr = "-";
        if ("1" == warnType) {
            warnTimeStr = ",";
        }
        List<Map> list = wmgMlService.getXmjgCsConfig(type);
        if (null != list && list.size() > 0) {
            if (StringUtils.equal("AM", timeType)) {
                String amStart = list.get(0).get("amTimeStart").toString();
                amStart = amStart.substring(amStart.indexOf(" "), amStart.indexOf(":")).trim();
                String amEnd = list.get(0).get("amTimeEnd").toString();
                amEnd = amEnd.substring(amEnd.indexOf(" "), amEnd.indexOf(":")).trim();
                result = "0 * " + amStart + warnTimeStr + amEnd + " * * ?";
                return result;
            } else {
                String pmStart = list.get(0).get("pmTimeStart").toString();
                pmStart = pmStart.substring(pmStart.indexOf(" "), pmStart.indexOf(":")).trim();
                String pmEnd = list.get(0).get("pmTimeEnd").toString();
                pmEnd = pmEnd.substring(pmEnd.indexOf(" "), pmEnd.indexOf(":")).trim();
                result = "0 * " + pmStart + warnTimeStr + pmEnd + " * * ?";
                return result;
            }
        }
        return result;
    }

    //初始化定时器任务
    private Map<String, CronTrigger> initMyTask() {
        Map<String, CronTrigger> mapTrigger = new ConcurrentHashMap();
        mapTrigger.put("C_WAIT_HANDLE_AM", new CronTrigger(getCron("C_WAIT_HANDLE", "AM", "1")));//投诉逾期上午定时提醒
        mapTrigger.put("C_WAIT_HANDLE_PM", new CronTrigger(getCron("C_WAIT_HANDLE", "PM", "1")));//投诉逾期下午定时提醒
        mapTrigger.put("S_WAIT_HANDLE_AM", new CronTrigger(getCron("S_WAIT_HANDLE", "AM", "2")));//建议未处理上午定时提醒
        mapTrigger.put("S_WAIT_HANDLE_PM", new CronTrigger(getCron("S_WAIT_HANDLE", "PM", "2")));//建议未处理下午定时提醒
        return mapTrigger;
    }

}

参考文章:
https://blog.csdn.net/qq_34125349/article/details/77430956
https://www.cnblogs.com/mmzs/p/10161936.html
https://www.cnblogs.com/zt007/p/8954096.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值