• 首页

  • 归档

  • 分类

  • 标签

  • 喵星人

  • 心情

  • 关于
W e A r e F a m i l y ! m i a o ~
W e A r e F a m i l y ! m i a o ~

柴子

青春流逝,记录往昔

04月
29
后端

定时跑批任务

发表于 2022-04-29 • 字数统计 12746 • 被 24 人看爆

JDK-Timer使用

简单示例

/**
 * 最简单用法
 */
@Test
public void timer1() {
    //创建timer,
    Timer timer = new Timer();
    //创建任务,类似与Runnable接口
    TimerTask task = new TimerTask() {
        @Override
        public void run() {
            System.out.println(LocalDateTime.now());
        }
    };
    //注入任务,参数配置:延迟时间,间隔时间
    timer.schedule(task, 0, 1000);
    while (true) {
    }
}

构造函数

Timer timer = new Timer();  //Timer是一个调度器
Timer timer = new Timer(String threadName);//threadName指定执行线程名称
Timer timer = new Timer(String threadName, boolean isDaemon); //threadName指定执行线程名称,isDaemon是否守护线程,设置为守护进程,将随进程的结束自动结束。

  • 修改线程名称

    /**
     * 修改执行线程名称
     */
    @Test
    public void timer2() {
        //创建timer,
        Timer timer = new Timer("简单定时器");
        //创建任务,类似与Runnable接口
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
                System.out.println(LocalDateTime.now());
            }
        };
        //注入任务,参数配置:延迟时间,间隔时间
        timer.schedule(task, 0, 500);
       // timer.scheduleAtFixedRate(task, 0, 500) //固定频率执行
        while (true) {
        }
    }
    
    //输出:
    //简单定时器
    //2022-04-27T14:19:32.204
    //简单定时器
    //2022-04-27T14:19:32.582
    
    

守护进程示例:

public static void main(String[] args) {
    Timer timer = new Timer("简单定时器",true); //第二参数,true表示为守护进程,false表示不是,也是默认值
    //创建任务,类似与Runnable接口
    TimerTask task = new TimerTask() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
            System.out.println(LocalDateTime.now());
        }
    };
    //注入任务,参数配置:延迟时间,间隔时间
    timer.scheduleAtFixedRate(task, 0, 500)
    System.out.println("程序结束");
}
/**
* 如果设置为非守护进程,输出结果如下:
* 程序结束
* 简单定时器
* 2022-04-29T10:27:13.992
* 简单定时器
* 2022-04-29T10:27:14.318
* 。。。    
*
*说明即使main进程结果,程序退出了,JVM还在跑这个定时任务。。。这样的话,就需要自己去控制线程的结束,如调用timer.cancle()
* 如果设置为守护进程,则输出结果如下:
* 程序结束
* 简单定时器
*
* Process finished with exit code 0
*/

说明

  • 简单的频率不高的定时任务

  • 不需要导入任何依赖

  • 轻量级

  • 单线程执行,可能会丢任务,如果陷入死循环,导致后续任务无法执行

JUC-ScheduledThreadPoolExecutor

简单示例

/**
 * 最简单用法
 * Runnable
 */
@Test
public void runnableTest(){
    ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(10);
    scheduledExecutorService.schedule(new Runnable() {
        @Override
        public void run() {
            System.out.println(LocalDateTime.now());
        }
    }, 1, TimeUnit.SECONDS);
    while (true){}
}

/**
 * 最简单用法
 * Callable
 * 获取返回值
 */
@Test
public void callableTest() throws ExecutionException, InterruptedException {
    ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(10);
    ScheduledFuture schedule = scheduledExecutorService.schedule(new Callable<LocalDateTime>() {
        @Override
        public LocalDateTime call() throws Exception {
            System.out.println(LocalDateTime.now());
            return LocalDateTime.now();
        }
    }, 1, TimeUnit.SECONDS);//延迟1秒,没秒执行一次
    Object obj = schedule.get();//阻塞获取返回值
    System.out.println("获取返回"+ obj);
    while (true){}
}
  • schedule 方法只执行一次

  • 更常用的为scheduleAtFixedRate 与scheduleWithFixedDelay

  • scheduleAtFixedRate 每次执行时间为上一次任务开始起向后推一个时间间隔,即每次执行时间为 :initialDelay, initialDelay+period, initialDelay+2period, …;

  • scheduleWithFixedDelay* 每次执行时间为上一次任务结束起向后推一个时间间隔,即每次执行时间为:initialDelay, initialDelay+executeTime+delay, initialDelay+2*executeTime+2*delay。

  • 由此可见,scheduleAtFixedRate 是基于固定时间间隔进行任务调度,scheduleWithFixedDelay 取决于每次任务执行的时间长短,是基于不固定时间间隔进行任务调度

/**
 * scheduleAtFixedRate
 */
@Test
public void scheduleAtFixedRateTest() throws ExecutionException, InterruptedException {
    ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(10);
    ScheduledFuture<?> future = scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            System.out.println(LocalDateTime.now());
        }
    }, 1, 1, TimeUnit.SECONDS);//延迟1秒,没秒执行一次
    future.get();//没有返回,阻塞
}

Spring @Scheduled

简单示例

@Component
@EnableScheduling
public class Schedules {
    @Scheduled(fixedDelay = 1000 * 2)//没2秒执行一次,基于上次任务结束时间往后推2秒
    //@Scheduled(fixedRate = 1000 * 2)//固定频率,当然如果任务执行的超过2秒,会堆积起来,任务执行完,立马执行堆积最久的任务。相比上面丢任务会少一点
    public void test1() throws InterruptedException {
        System.out.println("执行线程1:"+ Thread.currentThread().getName());
        System.out.println("定时任务执行1:"+ LocalDateTime.now());
    }
    
    @Scheduled(cron = "0/2 * * * * ?")
    public void test2(){
        System.out.println("执行线程2:"+ Thread.currentThread().getName());
        System.out.println("定时任务执行2:"+ LocalDateTime.now());
    }
}

Cron表达式

import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
@EnableScheduling
public class Schedules {

    @Scheduled(cron = "0/2 * * * * ?")
    public void test1() throws InterruptedException {
        Thread.sleep(5000);
        System.out.println("执行线程1:"+ Thread.currentThread().getName());
        System.out.println("定时任务执行1:"+ LocalDateTime.now());
    }

    @Scheduled(cron = "0/2 * * * * ?")
    public void test2(){
        System.out.println("执行线程2:"+ Thread.currentThread().getName());
        System.out.println("定时任务执行2:"+ LocalDateTime.now());
    }
}


// 输出
//执行线程2:scheduling-1
//定时任务执行2:2022-04-27T17:19:24.013
//执行线程1:scheduling-1
//定时任务执行1:2022-04-27T17:19:29.019
//执行线程2:scheduling-1
//定时任务执行2:2022-04-27T17:19:29.021
//执行线程2:scheduling-1
//定时任务执行2:2022-04-27T17:19:30.005
  • 从输出可以看出,定时任务是单线程执行的,定时任务1 的延时影响了定时任务2,所以需要@Async注解配合使用

配合@Async

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Component
@EnableScheduling
@EnableAsync
public class Schedules {


    @Async
    @Scheduled(cron = "0/2 * * * * ?")
    public void test1() throws InterruptedException {
        Thread.sleep(5000);
        System.out.println("执行线程1:"+ Thread.currentThread().getName());
        System.out.println("定时任务执行1:"+ LocalDateTime.now());
    }

    @Async
    @Scheduled(cron = "0/2 * * * * ?")
    public void test2(){
        System.out.println("执行线程2:"+ Thread.currentThread().getName());
        System.out.println("定时任务执行2:"+ LocalDateTime.now());
    }
}

//输出
//执行线程2:task-1
//定时任务执行2:2022-04-27T17:20:40.070
//执行线程2:task-3
//定时任务执行2:2022-04-27T17:20:42.006
//执行线程2:task-5
//定时任务执行2:2022-04-27T17:20:44.005
//执行线程1:task-2
//定时任务执行1:2022-04-27T17:20:45.072

自定义线程池

package com.chai.springAnnotation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
@EnableAsync
public class ExecutorConfig {

    @Bean
    public Executor myAsync() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(1000);//LinkedBlockingQueue
        executor.setThreadNamePrefix("myTask-");
        executor.initialize();
        return executor;
    }

}

@Component
@EnableScheduling
public class Schedules {


    @Async("myAsync")
    @Scheduled(cron = "0/2 * * * * ?")
    public void test1() throws InterruptedException {
        Thread.sleep(5000);
        System.out.println("执行线程1:"+ Thread.currentThread().getName());
        System.out.println("定时任务执行1:"+ LocalDateTime.now());
    }

    @Async("myAsync")
    @Scheduled(cron = "0/2 * * * * ?")
    public void test2(){
        System.out.println("执行线程2:"+ Thread.currentThread().getName());
        System.out.println("定时任务执行2:"+ LocalDateTime.now());
    }
}

//输出
//执行线程2:myTask-2
//定时任务执行2:2022-04-27T17:34:56.048
//执行线程2:myTask-3
//定时任务执行2:2022-04-27T17:34:58.008
//执行线程2:myTask-6
//定时任务执行2:2022-04-27T17:35:00.007
//执行线程1:myTask-1
//定时任务执行1:2022-04-27T17:35:01.049

Quartz框架

<!--   quartz    -->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
    <version>2.3.2</version>
</dependency>

简单示例

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.time.LocalDateTime;

public class MyJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println(this);
        System.out.println("任务执行" + LocalDateTime.now());
    }
}

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

public class QuartzTest {

    public static void main(String[] args) throws SchedulerException {
        // 1.创建调度器Scheduler
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        // 2.创建JobDetail实例
        JobDetail jobDetail = JobBuilder.newJob(MyJob.class).build();
        // 3.创建Trigger实例
        Trigger trigger = TriggerBuilder.newTrigger().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever()).build();
        // 4. 绑定scheduler的jobDetail与trigger
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start();
    }

}
//输出
//com.chai.quartz.MyJob@599a8d93
//任务执行2022-04-27T18:15:23.702
//com.chai.quartz.MyJob@6bbd20f6
//任务执行2022-04-27T18:15:24.698
//com.chai.quartz.MyJob@5dda8dcb
//任务执行2022-04-27T18:15:25.703

  • 从输出来看,每次打印的MyJob的hash值不一样,说明每次执行都会重新new一个MyJob

慢任务测试

  • 在MyJob增加延时
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.time.LocalDateTime;

public class MyJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(this);
        System.out.println("任务执行" + LocalDateTime.now());
    }
}

//输出
//com.chai.quartz.MyJob@1e0f4c37
//任务执行2022-04-27T18:20:49.904
//com.chai.quartz.MyJob@636eba44
//任务执行2022-04-27T18:20:50.905
//com.chai.quartz.MyJob@27bce3ea
//任务执行2022-04-27T18:20:51.907

  • 从输出来看,Quartz是默认多线程执行,慢任务不会影响下次执行时间

  • 但是我们大部分情况,希望任务能执行完一次再去执行一次,重复执行就废了

注解

  • @PersistJobDataAfterExecution

    • 告诉Quartz在成功执行了Job实现类的execute方法后(没有发生任何异常),更新JobDetail中JobDataMap的数据,使得该JobDetail实例在下一次执行的时候,JobDataMap中是更新后的数据,而不是更新前的旧数据。

    • 也就是说,你如果想跟新JobDataMap,并且让其下次执行就生效,那么增加这个注解

  • @DisallowConcurrentExecution

    • 告诉Quartz不要并发地执行同一个JobDetail实例。
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.time.LocalDateTime;
@DisallowConcurrentExecution
public class MyJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(this);
        System.out.println("任务执行" + LocalDateTime.now());
    }
}

//输出
//com.chai.quartz.MyJob@14247628
//任务执行2022-04-27T18:27:45.049
//com.chai.quartz.MyJob@7ad1dc45
//任务执行2022-04-27T18:27:47.060
//com.chai.quartz.MyJob@2eb81e7d
//任务执行2022-04-27T18:27:49.065

  • 从输出看,任务已经不会并发执行了

如果使用@PersistJobDataAfterExecution 那么一定要使用@DisallowConcurrentExecution ,否则并发情况下,可能任务1用的是旧的JobDataMap,任务2用的是新的。数据不一致

Cron表达式

    /**
     * cron
     * @throws SchedulerException
     */
    @Test
    public void test2() throws SchedulerException {
        // 1.创建调度器Scheduler
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        // 2.创建JobDetail实例
        JobDetail jobDetail = JobBuilder.newJob(MyJob.class).build();
        // 3.创建Trigger实例
        Trigger trigger = TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule("0/1 * * * * ?")).build();
        // 4. 绑定scheduler的jobDetail与trigger
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start();
        while (true) {
        }
    }

传参

import org.quartz.*;

import java.time.LocalDateTime;

@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class MyJob2 implements Job {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("任务执行" + LocalDateTime.now());
        System.out.println("--------------------jobDetail参数------------------------");
        System.out.println("name:"+context.getJobDetail().getKey().getName() + "---group : " + context.getJobDetail().getKey().getGroup());
        System.out.println("用户:"+ context.getJobDetail().getJobDataMap().getString("user"));
        System.out.println("---------------------trigger参数-----------------------");
        System.out.println("name:"+context.getTrigger().getKey().getName() + "---group : " + context.getTrigger().getKey().getGroup());
        System.out.println("id:"+ context.getTrigger().getJobDataMap().getInt("id"));
    }
}
/**
 * 传参
 * @throws SchedulerException
 */
@Test
public void test3() throws SchedulerException {
    // 1.创建调度器Scheduler
    SchedulerFactory schedulerFactory = new StdSchedulerFactory();
    Scheduler scheduler = schedulerFactory.getScheduler();
    // 2.创建JobDetail实例
    JobDetail jobDetail = JobBuilder.newJob(MyJob2.class).withIdentity("j1","g1")
            .usingJobData("user","chai").build();
    // 3.创建Trigger实例
    Trigger trigger = TriggerBuilder.newTrigger()
            .withIdentity("t1","tg1").usingJobData("id",123)
            .withSchedule(CronScheduleBuilder.cronSchedule("0/1 * * * * ?")).build();
    // 4. 绑定scheduler的jobDetail与trigger
    scheduler.scheduleJob(jobDetail, trigger);
    scheduler.start();
    while (true) {
    }
}

//输出
//任务执行2022-04-27T18:55:49.065
//--------------------jobDetail参数------------------------
//name:j1---group : g1
//用户:chai
//---------------------trigger参数-----------------------
//name:t1---group : tg1
//id:123

分享到:
设计模式-观察者
Vue2使用wangEditor5
  • 文章目录
  • 站点概览
柴子

内蒙 柴子

what do U want?

Github QQ Email RSS
最喜欢的电影
最喜欢的游戏
最喜欢的音乐
最喜欢的图书
最喜欢的动漫
夏洛特的烦恼
英雄联盟
痴心绝对
数据库从入门到删库跑路
斗破苍穹
看爆 Top5
  • 微信getUserProfile兼容性调整以及uniapp写法 1,866次看爆
  • gateway转发微服务请求丢失header参数 855次看爆
  • mybatis-plus代码生成器 849次看爆
  • Spring Boot Security从入门到进阶到高级 444次看爆
  • 物业报修系统设计-简化版 425次看爆
转载注明出处

站点已萌萌哒运行 00 天 00 小时 00 分 00 秒(●'◡'●)ノ♥

Copyright © 2022 柴子 京ICP备17035556号-1

由 Halo 强力驱动 · Theme by Sagiri · 站点地图