Java实现定时任务最简单的3种方法

 更新时间:2022年06月13日 11:52:31   作者:深海呐  
几乎在所有的项目中,定时任务的使用都是不可或缺的,如果使用不当甚至会造成资损,下面这篇文章主要给大家介绍了关于Java实现定时任务最简单的3种方法,本文通过实例代码介绍的非常详细,需要的朋友可以参考下

一、Timer

Timer是JAVA自带的定时任务类,实现如下:

public class MyTimerTask {    
    public static void main(String[] args) {        
        // 定义一个任务       
        TimerTask timerTask = new TimerTask() {            
        @Override            
            public void run() {                
            System.out.println("打印当前时间:" + new Date());    
            }       
         };        
        // 计时器       
        Timer timer = new Timer();       
        // 开始执行任务 (延迟1000毫秒执行,每3000毫秒执行一次)        
        timer.schedule(timerTask, 1000, 3000);    
    }
}

Timer 优缺点分析

优点是使用简单,缺点是当添加并执行多个任务时,前面任务的执行用时和异常将影响到后面任务,这边深海建议谨慎使用。

二、ScheduledExecutorService

ScheduledExecutorService 也是Java自带的类,

它可以实现Timer具备的所有功能,并解决了 Timer类存在的问题

实现如下:

public class MyScheduledExecutorService {    
    public static void main(String[] args) {        
        // 创建任务队列   10 为线程数量      
        ScheduledExecutorService scheduledExecutorService = 
                Executors.newScheduledThreadPool(10); 
        // 执行任务      
        scheduledExecutorService.scheduleAtFixedRate(() -> {          
            System.out.println("打印当前时间:" + new Date());      
        }, 1, 3, TimeUnit.SECONDS); // 1s 后开始执行,每 3s 执行一次   
 
  }
}

ScheduledExecutorService 优缺点分析

优点是,该类是JDK1.5自带的类,使用简单,缺点是该方案仅适用于单机环境。

三、Spring Task

Spring系列框架中Spring Framework自带的定时任务,

使用上面两种方式,很难实现某些特定需求,比如每周一执行某任务,但SpringTask可轻松实现。

以SpringBoot为例来实现:

1、开启定时任务

在SpringBoot的启动类上声明 @EnableScheduling:

@SpringBootApplication
@EnableScheduling //开启定时任务
public class DemoApplication {  
     // --  -- 
}

2、添加定时任务

只需使用@Scheduled注解标注即可,

如果有多个定时任务,可以创建多个@Scheduled标注的方法,示例如下:

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component // 把此类托管给 Spring,不能省略
public class TaskUtils {    
    // 添加定时任务    
    @Scheduled(cron = "30 40 23 0 0 5") // cron表达式:每周一 23:40:30 执行    
    public void doTask(){        
        System.out.println("我是定时任务~");    
    }
}

 Spring Boot 启动后会自动加载并执行定时任务,无需手动操作。

Cron 表达式

Spring Task 的实现需要使用 cron 表达式来声明执行的频率和规则,cron 表达式是由 6 位或者 7 位组成的(最后一位可以省略),每位之间以空格分隔,每位从左到右代表的含义如下:

其中 * 和 ? 号都表示匹配所有的时间。

cron 表达式在线生成地址:https://cron.qqe2.com/

知识扩展:分布式定时任务

上面的方法都是关于单机定时任务的实现,如果是分布式环境可以使用 Redis 来实现定时任务。

使用 Redis 实现延迟任务的方法大体可分为两类:通过 ZSet 的方式和键空间通知的方式

1、ZSet 实现方式

通过 ZSet 实现定时任务的思路是,将定时任务存放到 ZSet 集合中,并且将过期时间存储到 ZSet 的 Score 字段中,然后通过一个无线循环来判断当前时间内是否有需要执行的定时任务,如果有则进行执行,具体实现代码如下:

import redis.clients.jedis.Jedis;
import utils.JedisUtils;
import java.time.Instant;
import java.util.Set;
public class DelayQueueExample {        
    private static final String _KEY = "DelayQueueExample";        
    public static void main(String[] args) throws InterruptedException {        
        Jedis jedis = JedisUtils.getJedis();        
        // 30s 后执行        
        long delayTime = Instant.now().plusSeconds(30).getEpochSecond();       
        jedis.zadd(_KEY, delayTime, "order_1");        
        // 继续添加测试数据        
       jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_2");       
      jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_3");        
      jedis.zadd(_KEY, Instant.now().plusSeconds(7).getEpochSecond(), "order_4");        
     jedis.zadd(_KEY, Instant.now().plusSeconds(10).getEpochSecond(), "order_5");        
        // 开启定时任务队列        
        doDelayQueue(jedis);    
    }    
    /**     
    * 定时任务队列消费     
    * @param jedis Redis 客户端     
    */    
    public static void doDelayQueue(Jedis jedis) throws InterruptedException {        
        while (true) {            
            // 当前时间            
            Instant nowInstant = Instant.now();            
            long lastSecond = nowInstant.plusSeconds(-1).getEpochSecond(); 
            // 上一秒时间            
            long nowSecond = nowInstant.getEpochSecond();            
            // 查询当前时间的所有任务            
            Set data = jedis.zrangeByScore(_KEY, lastSecond, nowSecond);            
            for (String item : data) {                
            // 消费任务                
            System.out.println("消费:" + item);            
        }            
        // 删除已经执行的任务            
        jedis.zremrangeByScore(_KEY, lastSecond, nowSecond);            
        Thread.sleep(1000); // 每秒查询一次        
        }    
    }
}

2、键空间通知

我们可以通过 Redis 的键空间通知来实现定时任务,它的实现思路是给所有的定时任务设置一个过期时间,等到了过期之后,我们通过订阅过期消息就能感知到定时任务需要被执行了,此时我们执行定时任务即可。

默认情况下 Redis 是不开启键空间通知的,需要我们通过 config set notify-keyspace-events Ex 的命令手动开启,开启之后定时任务的代码如下:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
import utils.JedisUtils;
public class TaskExample {    
    public static final String _TOPIC = "__keyevent@0__:expired"; // 订阅频道名称   
    public static void main(String[] args) {       
        Jedis jedis = JedisUtils.getJedis();       
        // 执行定时任务        
        doTask(jedis);    
    }   
     /**     
       * 订阅过期消息,执行定时任务     
       * @param jedis Redis 客户端     
       */    
    public static void doTask(Jedis jedis) {        
        // 订阅过期消息        
        jedis.psubscribe(new JedisPubSub() {            
            @Override            
 public void onPMessage(String pattern, String channel, String message) {                
            // 接收到消息,执行定时任务                
            System.out.println("收到消息:" + message);            
            }            
        }, _TOPIC);    
    }
}

总结

到此这篇关于Java实现定时任务最简单的3种方法的文章就介绍到这了,更多相关Java定时任务实现内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot文件上传大小设置方式(yml中配置)

    SpringBoot文件上传大小设置方式(yml中配置)

    这篇文章主要介绍了SpringBoot文件上传大小设置方式(yml中配置),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • SpringBoot Admin使用及心跳检测原理分析

    SpringBoot Admin使用及心跳检测原理分析

    这篇文章主要介绍了SpringBoot Admin使用及心跳检测原理分析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • java递归法求字符串逆序

    java递归法求字符串逆序

    这篇文章主要介绍了java递归法求字符串逆序,涉及java递归调用的相关操作技巧,需要的朋友可以参考下
    2015-05-05
  • springboot 中整合mybatis多数据源不使用JPA

    springboot 中整合mybatis多数据源不使用JPA

    这篇文章主要介绍了springboot 中整合mybatis多数据源不使用JPA,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • MyBatis中如何查询某个时间段内的数据

    MyBatis中如何查询某个时间段内的数据

    这篇文章主要介绍了MyBatis中如何查询某个时间段内的数据,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • lombok插件无法使用的原因及解决方案

    lombok插件无法使用的原因及解决方案

    这篇文章主要介绍了lombok插件无法使用的原因及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • spring-boot整合dubbo:Spring-boot-dubbo-starter

    spring-boot整合dubbo:Spring-boot-dubbo-starter

    这篇文章主要介绍了spring-boot整合dubbo:Spring-boot-dubbo-starter的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-05-05
  • Java的Lock接口与读写锁详解

    Java的Lock接口与读写锁详解

    这篇文章主要介绍了Java的Lock接口与读写锁详解,锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源,但是有些锁可以允许多个线程并发的访问共享资源,比如读写锁,需要的朋友可以参考下
    2023-12-12
  • kafka分布式消息系统基本架构及功能详解

    kafka分布式消息系统基本架构及功能详解

    这篇文章主要为大家介绍了kafka分布式消息系统基本架构及功能详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • 一文带你详细认识文件与Java中操作文件

    一文带你详细认识文件与Java中操作文件

    文件处理是任何应用程序的重要部分,Java 提供了许多用于创建、读取、更新和删除文件的方法,这篇文章主要给大家介绍了关于认识文件与Java中操作文件的相关资料,需要的朋友可以参考下
    2024-05-05

最新评论