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定时任务_定时任务最简单的3种实现方法(超好用)
定时任务在实际的开发中特别常见,比如电商平台 30 分钟后自动取消未支付的订单,以及凌晨的数据汇总和备份等,都需要借助定时任务来实现,那么我们本文就来看一下定时任务最简单的几种实现方式. TOP 1: ...
- java 客户端定时任务_定时任务最简单的3种实现方法(超实用)
定时任务在实际的开发中特别常见,比如电商平台 30 分钟后自动取消未支付的订单,以及凌晨的数据汇总和备份等,都需要借助定时任务来实现,那么我们本文就来看一下定时任务最简单的几种实现方式. TOP 1: ...
- JAVA定时任务的简单实现
Java定时任务的简单实现 2011-01-02 18:34:43| 分类: 软件开发 | 标签:void timer import param dateutil |字号大中小 订阅 ...
- JAVA中线程同步的几种实现方法
JAVA中线程同步的几种实现方法 一.synchronized同步的方法: 1.synchronized同步方法 即有synchronized关键字修饰的方法. 由于java的每个对象都有一个内置锁, ...
- java调用javascript函数_[Java教程]JavaScript函数的4种调用方法详解
[Java教程]JavaScript函数的4种调用方法详解 0 2016-08-09 00:00:12 在JavaScript中,函数是一等公民,函数在JavaScript中是一个数据类型,而非像C# ...
- java map集合遍历方法,Java的Map集合的三种遍历方法
集合的一个很重要的操作---遍历,学习了三种遍历方法,三种方法各有优缺点~~ 1. package com.myTest.MapText; import java.util.Collection; i ...
- Java设计模式之 简单工厂模式和工厂方法实验报告书
目录 Java设计模式之 1 简单工厂模式和工厂方法实验报告书 1 实验四:简单工厂模式和工厂方法 2 一.实验目的 2 二.实验内容 2 三.实验步骤 2 3.1简单工厂模式:女娲 2 3.2简单工 ...
- Java里阻塞线程的三种实现方法
在日常开发中,我们有时会遇到遇到多线程处理任务的情况,JDK里提供了便利的 ThreadPoolExecutor以及其包装的工具类Executors.但是我们知道 ExecutorService.ex ...
- Java使用付费代理的两种实现方法
免费代理,如西刺代理.快代理等代理网站公布的代理地址,常常存在几个问题: 1. 存在反扒措施,如限制爬虫频率.存在并发数限制.封IP等: 2. 免费代理存在时效性,无法长时间使用. 目前获得稳 ...
- JAVA中运用数组的四种排序方法
JAVA中在运用数组进行排序功能时,一般有四种方法:快速排序法.冒泡法.选择排序法.插入排序法. 快速排序法主要是运用了Arrays中的一个方法Arrays.sort()实现. 冒泡法是运用遍历数组进 ...
最新文章
- 有关Run-Time Check Failure #2 - Stack around the variable 'XXX' was corrupted.错误的解决方法
- 「每周论文推荐」 初入深度学习CV领域必读的几篇文章
- java软件设计模式只单例设计模式
- Fiori GM4取metadata 从http自动重定向到https原因
- python入门文件读取与写入_初学者Python:读取和写入同一文件
- MySQL(介绍,安装,密码操作,权限表)
- superset0.34源码级别汉化
- mysql workbench中文设置_使用Workbench完成流体压力渗透分析
- 59 MM配置-后勤发票校验-维护税代码缺省值
- Storm-Engine 基于 C++ 的开源游戏引擎
- 几款好用的串口和网络调试助手
- 中了磁碟机病毒,惨啊!!!
- win10系统从旧固态迁移到新固态,win10系统迁移到固态硬盘ssd
- 面对tomatserver使用的webrequest
- 如何训练神经网络的参数?,神经网络的参数计算
- 数据库原理 封锁的粒度
- 使用 MitmProxy 玩爬虫的,这篇文章别错过了!
- 史上最全网络安全面试题合集
- sqlite3数据库的使用及其对应的API函数接口的使用
- 前端中的hack是什么意思?常见的hack技术以及以及hack技术的利弊