java定时任务

2023-10-17 08:18:39 浏览数 (2)

引言:知易行难

这里我推荐使用第一种,Spring定时任务,简单又简介,高效

一、Spring定时任务

基于springboot创建一个项目,使用定时任务很简单

俩步即可实现

代码语言:javascript复制
1、启动类上加注解@EnableScheduling注解开启定时任务
2、方法上加注解@Scheduled设置任务执行时间

示例 默认是单线程的定时任务

代码语言:javascript复制
@Scheduled(fixedDelay =5*1000 )  // 每隔五秒执行一次  单位毫秒 1秒 = 1000毫秒
public void sendMsg(){
       System.out.println("当前线程名:" Thread.currentThread().getName());
       System.err.println(DateFormat.getDateTimeInstance().format(new Date())  "t多喝热水");
}

这里设置的是 时间间隔 fixedDelay 和 fixedRate 效果等同

代码语言:javascript复制
fixedRate和fixedDelay区别:
fixedRate:它的间隔时间是根据上次任务开始的时候计时。
	例如:fixedRate = 5 * 1000 执行该方法所花的时间是2秒,那么3秒后就会再次执行该方法。
fixedDelay:它的间隔时间是根据上次的任务结束的时候开始计时的。
	例如:一个方法设置了 fixedDelay = 5*1000 当该方法某一次执行结束后,开始计算时间,当时间达到5秒,就开始再次执行该方法。

多线程实现,也很简单

代码语言:javascript复制
1、启动类上开启异步注解	@EnableAsync
2、在定时任务的方法上加注解,设置异步执行	@Async 

示例:每三秒执行一次

代码语言:javascript复制
@Scheduled(cron ="0/3 * * * * ?")
@Async
public void ser(){
        System.out.println("当前线程名:" Thread.currentThread().getName());
        System.err.println(DateFormat.getDateTimeInstance().format(new Date()) "t每3秒的时候执行");
}

cron表达式 在线生成cron表达式的网站:在线cron表达式

只要知道 , - * / 这四个和 ? 号 这五个就可以了,其他的了解一下即可

代码语言:javascript复制
通用符号:	, - * /
逗号表示枚举值,例如:在Minutes域使用5,20 表示在分钟数为5, 20的时候触发事件
减号表示范围,  例如:在Minutes域使用5-20 表示在分钟数为5到20的时候每分钟都触发一次事件
*号表示该域的任意值,假如在Minutes域使用* 表示分钟数不受限制,每分钟都触发事件
/号表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20表示时间的分钟数为5的时候触发一次,后隔20分钟触发一次,即 25触发执行一次、45触发执行一次。

专有符号:在Spring定时任务中,除了问号,其他都不支持!
?	问号:只能用在日和星期俩个域,他俩互斥,必须对其中一个进行设置,使用的场景不关心这个值
L	大写字母L,只能出现在日和星期俩个域,如果在DayOfWeek使用5L,意味着在最后的一个星期四触发
W	大写字母W,表示有效工作日(周一到周五),只能出现在DayOfMonth域,系统将在离指定日期的最近有效工作日触发事件
LW	这俩个字符可以连用,表示在某个月最后一个工作日
#	用于确定每个月的第几个星期几,只能出现在DayofWeek域,例如:4#2,表示某月的第二个星期三
C	只能用在DayofMonth和DayofWeek俩个域,需要关联日历,如果没关联可以忽略。

拓展一下: @Scheduled()的8个参数的意思

代码语言:javascript复制
1、cron
	接受一个cron表达式
2、zone
	时区,接受一个java.util.TimeZone#ID
	默认是一个空字符串,取服务器所在地的时区,该字段一般留空,我们一般使用的时区:Asia/Shanghai
3、fixedDelay
	上一次执行完 间隔多长时间再次执行
4、fixedDelayString
	和fixedDelay 意思相同,只是使用字符串的形式,唯一不同的是支持占位符
5、fixedRate
	上一次开始执行时间点之后多长时间再执行
6、fixedRateString
	与fixedRate 意思相同,只是使用字符串的形式,唯一不同的是支持占位符
7、initialDelay
	第一次延迟多长时间后再执行
8、initialDealyString
	与initialDelay 意思相同,只是使用字符串的形式,唯一不同的是支持占位符

二、JDK自带的Timer

使用也是两步

代码语言:javascript复制
1、创建Timer对象
2、执行schedule方法

示例1

代码语言:javascript复制
public static void main(String[] args) {
    // 创建timer对象
    Timer timer = new Timer();

    // 执行定时任务
    // 参数1  timerTask对象 定时任务对象
    // 参数2  任务什么时候启动
    // 参数3  执行任务的时间间隔
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("定时任务1===t"  DateFormat.getDateTimeInstance().format(new Date()));
        }
    },new Date(),1000);
}

示例2

代码语言:javascript复制
public static void main(String[] args) {
        // 创建timer对象   任务启动
        Timer timer = new Timer();
        for (int i = 0; i < 3; i  ) {
            StudyTimerTask studyTimerTask = new StudyTimerTask("执行者" i "号");
            // 立刻执行  每隔2秒执行一次  任务添加
            timer.schedule(studyTimerTask,new Date(),2000);
//            timer.schedule();     		丢任务  少执行
//            timer.scheduleAtFixedRate();  提前执行,执行时间会乱
            // 单线程  任务阻塞  任务超时
        }
    }

//另一个类
class StudyTimerTask extends TimerTask{

    private String name;

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

    @Override
    public void run() {
        try {
            System.out.println(name "t任务开始时间t" DateFormat.getDateTimeInstance().format(new Date()));
            Thread.sleep(5000);
            System.err.println(name "t任务结束时间t" DateFormat.getDateTimeInstance().format(new Date()));
            // 线程池执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

多线程模拟示例 示例3

代码语言:javascript复制
 public static void main(String[] args) {
       // 线程池执行
       // 先创建一个线程池
        ScheduledExecutorService scheduledThreadPool  = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 2; i  ) {
            StudyTimerTask2 task2 = new StudyTimerTask2("多线程启动"   i   "号");
            // 参数1 任务  参数2 延迟时间  参数3 间隔时间  参数4 时间单位
            scheduledThreadPool.scheduleAtFixedRate(task2,0,2, TimeUnit.SECONDS);
        }
    }

class StudyTimerTask2 implements Runnable{

    private String name;

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

    @Override
    public void run() {
        try {
            System.out.println(name "t任务开始时间t" DateFormat.getDateTimeInstance().format(new Date()));
            Thread.sleep(5000);
            System.err.println(name "t任务结束时间t" DateFormat.getDateTimeInstance().format(new Date()));
            // 线程池执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

三、quartz框架

这里我记录一下源生的quartz的使用

使用详情

代码语言:javascript复制
1、创建一个类实现Job
2、调用(JobDetail 和Trigger)	这个比较复杂一点

导入依赖

代码语言:javascript复制
<dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
</dependency>

@DisallowConcurrentExecution和@PersistJobDataAfterExecution的解释:

代码语言:javascript复制
禁止并发地执行同一个job定义(jobDetail定义的多个实例) 加在类上面
@DisallowConcurrentExecution


如果一个任务不是持久化的,则当没有触发器关联它的时候,quartz会从scheduled删除它
将参数进行持久化(对trigger中的dataMap无效)
@PersistJobDataAfterExecution

创建一个job

代码语言:javascript复制
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class MyJob implements Job {

    // 另一种存值的方式
    private String address;

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        JobDataMap jobDetailMap = jobExecutionContext.getJobDetail().getJobDataMap();
        JobDataMap jobTriggerMap = jobExecutionContext.getTrigger().getJobDataMap();
        jobDetailMap.put("count",jobDetailMap.getIntValue("count") 1);
        System.out.println("jobDetailMap=t" jobDetailMap.getString("name"));
        System.out.println("jobDetailMap=t" jobDetailMap.getString("age"));
        System.out.println("jobTriggerMap=t" jobTriggerMap.getString("sex"));
        // 要是有键名相同的话  这里会覆盖掉
        JobDataMap mergedJobDataMap = jobExecutionContext.getMergedJobDataMap(); // 就是把前俩个的map都获取
        System.err.println("address=t" address);
        System.err.println("调的次数count=t" jobDetailMap.getInt("count"));
        System.err.println("mergedJobDataMaptsex=" mergedJobDataMap.get("sex") "tname=" mergedJobDataMap.get("name"));
        System.err.println("MyJob is executet"  DateFormat.getDateTimeInstance().format(new Date()));
        // 测试每次调用任务的时候都创建一个新的实例
        System.out.println("jobDetail:t" System.identityHashCode(jobExecutionContext.getJobDetail()));
        System.out.println("job:t" System.identityHashCode(jobExecutionContext.getJobInstance()));
    }
}

启动

使用usingJobData 进行传参,可以理解为往map中添加键值对

代码语言:javascript复制
public class TestJob {
    public static void main(String[] args) {
        int count=0; // 统计定时任务调了几次

        JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
                .withIdentity("任务名叫小明","任务组为group1")
                // 理解为启动定时任务的时候 往业务逻辑传递一些参数
                .usingJobData("name","张三")     // 存放map 键值的形式  任务可以获取参数
                .usingJobData("age","18")
                .usingJobData("count",count)
                .usingJobData("address","上海浦东区")
                .build();
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("触发器1","触发器组1")
                .usingJobData("sex","男")   // 设置键值
                // Job 通过set设置值的话, trigger的值会覆盖jobDetail的值
                .usingJobData("address","北京市")
                .startNow()  // 立即启动  指定时间启动的话 startAt
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1)
                        .repeatForever())        // 一直执行
                        .build();

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

0 人点赞