定时任务框架初步学习

9.10 定时任务框架初步了解
阅读框架实现方式,有助于更好的使用框架,理解设计思想。

一、基本使用

了解定时任务的基本使用

1.1 引入框架源码

在这里插入图片描述

1.2 调用SchedulerManager创建定时任务

该类是定时任务的线程管理工具,可通过此类提供的方法,创建定时任务,并加入线程队列

	schedulerManager.schedule("activitybegin-job", "0 0/5 * * * ?", new ScheduledJob() {
		@Override
		private void run(){}; // 业务代码

		@Override
		private void terminate(){}; // 终止线程方式,应用于线程引擎终止时每个线程的销毁方式
	}

调用schedulerManager.schedule方法创建一个定时任务线程,该方法需要传递三个参数

  • jobName:定时任务的名称,后缀要与配置的job.type一致(application.properties)
  • crontab表达式:周期执行模式
  • ScheduleJob:线程任务(在定时任务线程中被执行),内部需要实现run、terminate两个方法,run内写业务代码、terminate内写终止工作线程方式

1.3 crontab表达式

通过书写crontab表达式,可以指定线程周期执行的模式,比如每隔5秒执行一次,每个小时的前五分钟的每5秒执行一次……

如上 “0 0/5 * * * ?” 代表的是每星期的每一天,每隔五分钟执行一次。
crontab表达式由一组连续的数字或字符组成,他们之间通过一个空格分隔。从左到右依次代表
秒 分钟 小时 天 月份 每周的第几天 年(可以省略)
以下是标识范围的的字符形式,0/x表示的是间隔,执行周期为x。 * 代表所有的时间段。 - 代表在什么什么范围内。?代表任意。
在这里插入图片描述

二、整体分析:类结构分析

理解类结构,是理解框架细节的前提。

  • ScheduledJob:线程任务接口,我们的定时任务业务就是写在这个实现类里
  • Scheduler:定时任务引擎,它包含一个内部类ScheduledJobThread,作为我们线程任务执行的载体。内部维护了一个ScheduledJobThread集合。
  • SchedulerManager:根据配置,决定是否初始化Scheduler、但前定时任务线程的名字是否合法。它是我们直接操作的对象,可以通过它来创建定时任务。
  • JobConfig:配置类,用于获取job前缀的配置属性。配置属性包括type:工作类型,whiteList:白名单。
  • CronExpression:定时异常类。

三、具体分析:框架逻辑分析

按照程序执行流程,来理解框架的逻辑。

3.1 SchedulerManager

内部维护的属性:

  • JobConfig:配置信息
  • EnvironmentService:开发环境service,用于判断当前开发环境。
  • mqEnabled:是否开启消息队列
  • Scheduler:定时任务引擎

初始化动作:

	@PostConstruct
    public void init()
    {
        boolean jobSchedule = "job".contentEquals(config.getType());
        if (jobSchedule)
        {
            instance = new Scheduler();
            L.warn("Enable job-executing");
        }
        mqEnabled = jobSchedule;
        if (mqEnabled)
        {
            if (CollectionUtils.isNotEmpty(config.getWhitelist()) && config.getWhitelist().contains("!mq"))
            {
                mqEnabled = false;
            }
        }
    }
  1. 判断配置中定义的工作类型是否为“job”
  2. 如果是,创建Schedule对象
  3. 如果白名单不为空,且白名单中包含**!mq**则不开启消息队列,默认是开启的

schedule

  1. 如果初始化没有创建Scheduler,即配置中定义的工作类型不为“job",则直接返回。
  2. 判断jobname是否合法,(如果白名单为空,则所有名字都合法;如果白名单不为空,截取.或_前面的内容为module,如果module还包含._-则是一个错误的jobname,查看module是否在白名单中,如果不在就不合法。)
  3. 如果名字合法,则调用内部维护的Scheduler的schedule方法,将该定时任务载入线程中执行。
  4. 此处还有一个判断,如果当前环境是开发环境,默认的使用每五秒执行一次策略,其他环境的话按照我们传递的crontab执行。

3.2 Scheduler

内部维护属性

  • List:定时任务线程集合
  • stopped:终止所有线程标识。

初始化动作

    public Scheduler()
    {
        Runtime.getRuntime().addShutdownHook(new Thread("destroy") {
            public void run()
            {
                shutdown();
            }
        });
    }

销毁动作(优雅解决)
创建一个名为destroy(销毁)的线程,先调用所有定时任务自己的销毁方式。并释放所有当前正在等待执行的线程的锁,并通过join让所有线程依次执行销毁。

	private void shutdown()
    {
        synchronized (threads)
        {
            stopped = true;
            for (ScheduledJobThread thread : threads)
            {
                thread.shutdown();
            }
        }
    }
    
	public void shutdown()
        {
            L.warn("[" + jobName + "] terminating");
            stopped = true;
            try
            {
                job.terminate();
            } catch (Exception e)
            {
                LOG.error(null, e);
            }
            synchronized (this)
            {
                try
                {
                    notify();
                } catch (Exception e)
                {
                    LOG.error(null, e);
                }
            }
            try
            {
                join();
            } catch (Exception e)
            {
                L.error(null, e);
            }
            LOG.warn("[" + jobName + "] is terminated");
        }
    }

schedule

创建ScheduledJobThread并将线程添加进入线程队列中(threads)启动线程(之前判断该线程引擎是否停止)

3.3 Scheduler -> ScheduledJobThread

初始化动作

  • 修改线程名称为job_schedule
  • 判断crontab表达式是否合法

run()

线程内部一直轮询执行job.run(也就是我们传递过来的定时任务业务),直到当前线程引擎终止。

			while (!stopped)
            {
                try
                {
                    Date lastCompletedTime = new Date();
                    waitUntilScheduled(lastCompletedTime);
                    if (stopped)
                    {
                        break;
                    }
                    job.run();
                } catch (Throwable e)
                {
                    LOG.error(null, e);
                    try
                    {
                        Thread.sleep(1000);
                    } catch (Throwable t)
                    {
                        // ignore
                    }
                }
            }

如果线程引擎没有停止,一直执行定时任务。
waitUntilScheduled()

        private void waitUntilScheduled(Date afterTime)
        {
            if (stopped)
            {
                return;
            }
            Date scheduleTime = crontab.getNextValidTimeAfter(afterTime);
            long delay = scheduleTime.getTime() - System.currentTimeMillis();
            if (LOG.isInfoEnabled())
            {
                log("schedule at " + scheduleTime + ", delay " + TimeUnit.MILLISECONDS.toSeconds(delay) + "s");
            }
            if (delay <= 0)
            {
                return;
            }
            synchronized (this)
            {
                try
                {
                    wait(delay);
                } catch (InterruptedException e)
                {
                    LOG.error(null, e);
                }
            }
        }

通过crontab.getNextValidTimeAfter计算出下一次执行定时任务的时间。并减去当前时间获取需要间隔多久来执行下一次定时任务。
通过wait(delay)延期执行下一次任务。(锁的是当前线程)。
基本上每段代码之间都对于线程引擎运行状态进行了判断,最大程度控制了线程开关的准确性。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值