java中的定时任务

一、理论基础(底层算法)

1、小顶堆
小顶堆是一颗完全二叉树(除了最后一层外其它层都达到最大节点数,且最后一层节点都是靠左排列);
堆中某个节点的值总是不大于其父节点的值,也就是说根节点的值是最小的。

堆化:
    插入元素:插入尾部,然后上浮。
    删除对顶元素:将尾部(最大的)元素放到堆顶,然后下沉。
2、时间轮算法

(1)链表或者数组实现时间轮:遍历数组,每个下标放置一个链表,链表节点放置任务,遍历到了就取出执行。
(2)round型时间轮:任务上记录一个round,遍历到了就将round减1,为0时取出执行,但由于需要遍历所有任务,效率较低。
(3)分层时间轮:使用多个不同时间纬度的轮。例如月轮遍历到了,将任务取出放到天轮里面,即可实现几号几点执行。

二、JDK定时器timer使用

package com.tuling.timer;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerTest {
    public static void main(String[] args) {
        Timer t = new Timer();//任务启动
        for (int i=0; i<2; i++){
            TimerTask task = new FooTimerTask("foo"+i);
            t.scheduleAtFixedRate(task,new Date(),2000);//任务添加   10s 5次   4 3
            // 预设的执行时间nextExecutTime 12:00:00   12:00:02  12:00:04
            //schedule  任务执行超时,会导致后面的任务往后推移,预想在这个间隔内存的任务执行就没了
            //scheduleAtFixedRate  任务超时可能导致下一个任务就会马上执行
            //单线程  任务阻塞  任务超时
        }
    }
}
class FooTimerTask extends TimerTask {

    private String name;

    public FooTimerTask(String name) {
        this.name = name;
    }

    public void run() {
        try {
            System.out.println("name="+name+",startTime="+new Date());
            Thread.sleep(3000);
            System.out.println("name="+name+",endTime="+new Date());

            //线程池执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

三、定时任务线程池

使用多线程执行任务,不会相互阻塞;如果线程失活,会新建线程执行任务。

package com.tuling.pool;

import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduleThreadPoolTest {

    public static void main(String[] args) {
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        for (int i=0;i<2;i++){
            scheduledThreadPool.scheduleAtFixedRate(new Task("task-"+i),0,2, TimeUnit.SECONDS);
        }
    }
}
class Task implements Runnable{

    private String name;

    public Task(String name) {
        this.name = name;
    }

    public void run() {
        try {
            System.out.println("name="+name+",startTime="+new Date());
            Thread.sleep(3000);
            System.out.println("name="+name+",endTime="+new Date());

            //线程池执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

四、定时任务框架之quartz

官网地址:http://www.quartz-scheduler.org/
在这里插入图片描述
@DisallowConcurrentExecution:禁止并发地执行同一个job定义(jobDetail定义)的多个实例。
@PersistJobDataAfterExecution:持久化jobDetail中的jobDateMap(对trigger中的datemap无效)。

package com.tuling.quartz;

import org.quartz.*;
import java.util.Date;

@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class MyJob implements Job {

    private String name;

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        /*JobDataMap jobDetailMap = context.getJobDetail().getJobDataMap();
        JobDataMap triggerMap = context.getTrigger().getJobDataMap();
        JobDataMap mergeMap = context.getMergedJobDataMap();
        System.out.println("jobDetailMap:"+jobDetailMap.getString("job"));
        System.out.println("triggerMap:"+triggerMap.getString("trigger"));
        System.out.println("mergeMap:"+mergeMap.getString("trigger"));

        System.out.println("name:"+name);*/

        /*System.out.println("jobDetail:"+System.identityHashCode(context.getJobDetail()));
        System.out.println("job:"+System.identityHashCode(context.getJobInstance()));*/

        /*System.out.println("execute:"+new Date());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/

        JobDataMap triggerMap = context.getTrigger().getJobDataMap();
        JobDataMap jobDetailMap = context.getJobDetail().getJobDataMap();
        triggerMap.put("count",triggerMap.getInt("count")+1);
        jobDetailMap.put("count1",jobDetailMap.getInt("count1")+1);
        System.out.println("triggerMap count:"+triggerMap.getInt("count"));
        System.out.println("jobDetailMap count:"+jobDetailMap.getInt("count1"));
    }
}
package com.tuling.quartz;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class TestJob {

    public static void main(String[] args) {
        JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
                .withIdentity("job1","group1")
                .usingJobData("job","jobDetail")
                .usingJobData("name","jobDetail")
                .usingJobData("count1",0)
                .build();

        int count=0;
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1","trigger1")
                .usingJobData("trigger","trigger")
                .usingJobData("count",count)
                //.usingJobData("name","trigger")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1)
                        .repeatForever())
                .build();

        try {
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            scheduler.scheduleJob(jobDetail,trigger);
            scheduler.start();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

五、触发器

优先级:同时触发的trigger之间才会比较优先级; 如果trigger是可恢复的,在恢复后再调度时,优先级不变。
calendar:用于排除时间段。

misfire(错过触发/失火):
   判断条件:job到达触发时间时没有被执行;
           被执行的延迟时间超过了quartz配置的misfireThreshold阈值。
   产生原因:在job需要触发的时间点,scheduler停止了;
           当job达到触发时间时,所有线程都被其他job占用,没有可用线程;
           job使用了@DisallowConcurrentExecution注解,job不能并发执行,当达到下一个job执行点的时候,上一个任务还没有完成;
           job指定了过去的开始执行时间。  
   策略: 默认使用MISFIRE_INSTRUCTION_SMART_POLICY策略。
         SimpleTrigger:
             now*相关策略,会立即执行第一个misfire的任务,同时会修改startTime和repeatCount,因此会重新计算finalFireTime,原计划执行时间会被打乱。
             next*相关策略,不会立即执行misfire的任务,也不会修改startTime和repeatCount,因此finalFireTime也没有改变,发生了misfire也还是按照原计划进行执行。
         CronTrigger:    
             MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY:quartz不会判断发生了misfire,立即执行所有发生了misfire的任务,然后按照原计划进行执行。
                例如:10:15分立即执行9:00和10:00的任务,然后等待下一个任务在11:00执行,后续按照原计划执行。
             MISFIRE_INSTRUCTION_DO_NOTHING:所有发生misfire的任务都被忽略,只是按照原计划继续执行。
             MISFIRE_INSTRUCTION_FIRE_NOW:立即执行第一个发生misfire的任务,忽略其他发生misfire的任务,然后按照原计划继续执行。
                例如:在10:15立即执行9:00任务,忽略10:00任务,然后等待下一个任务在11:00执行,后续按照原计划执行。

六、CronExpression

星期
允许的符号, - * /, - * /, - * /, - * / ? L W, - * /, - * / ? L #, - * /
允许的值0-590-590-231-310-11 或 JAN-DEC1-7 或 SUN-SAT1970-2199

(1):表示匹配该域的任意值。假如在Minutes域使用, 即表示每分钟都会触发事件。

(2)?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。

(3)-:表示范围。例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次

(4)/:表示起始时间开始触发,然后每隔固定时间触发一次。例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次.

(5),:表示列出枚举值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。

(6)L:表示最后,只能出现在DayofWeek和DayofMonth域。如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。

(7)W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在 DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份 。

(8)LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。

(9)#:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一条代码鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值