不用任何框架,Java 就能实现定时任务的 3 种方法!
是的,不用任何框架,用我们朴素的 Java 编程语言就能实现定时任务。
今天,栈长就介绍 3 种实现方法,教你如何使用 JDK 实现定时任务!
1、 sleep
这也是我们最常用的 sleep 休眠大法,不只是当作休眠用,我们还可以利用它很轻松的能实现一个简单的定时任务。
实现逻辑:
新开一个线程,添加一个 for/ while 死循环,然后在死循环里面添加一个 sleep 休眠逻辑,让程序每隔 N 秒休眠再执行一次,这样就达到了一个简单定时任务的效果。
实现代码如下:
/**
* 休眠实现定时任务
* 来源公众号:Java技术栈
*/
private static void sleepTask() {
new Thread(() -> {
while (true) {
System.out.println("hi, 欢迎关注:Java技术栈");
try {
// 每隔3秒执行一次
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
这种方式比较傻瓜化了,只能按固定频率运行,不能指定具体运行的时间。
另外,上面的箭头语法,栈长使用了 JDK 8 中的 Lambda 表达式,这里就不再撰述了,Java 8 系列实战教程我都写了一堆了,不清楚的可以关注公众号:Java技术栈,在后台回复 "java" 阅读,我都整理好了。
2、Timer
来看下 JDK 自带的 java.util.Timer 类:

JDK 1.3 就内置了 java.util.Timer 类,可以用来调度 java.util.TimerTask 任务。

几个重要的方法:
- schedule:开始调度任务,提供了几个包装方法;
- cancle:终止任务调度,取消当前调度的所有任务,正在运行的任务不受影响;
- purge:从任务队列中移除所有已取消的任务;
另外,java.util.TimerTask 就是实现了 Runnable 接口,具体任务逻辑则是在 run 方法里去实现。

实现代码如下:
/**
* timer定时任务
* 来源公众号:Java技术栈
*/
private static void timerTask() throws InterruptedException {
Timer timer = new Timer();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("hi, 欢迎关注:Java技术栈");
}
};
// 第一次任务延迟时间
long delay = 2000;
// 任务执行频率
long period = 3 * 1000;
// 开始调度
timer.schedule(timerTask, delay, period);
// 指定首次运行时间
// timer.schedule(timerTask, DateUtils.addSeconds(new Date(), 5), period);
Thread.sleep(20000);
// 终止并移除任务
timer.cancel();
timer.purge();
}
这种实现方式比较简单,可以指定首次执行的延迟时间、首次执行的具体日期时间,以及执行频率,能满足日常需要。
另外,需要注意的是,Timer 是线程安全的,因为背后是单线程在执行所有任务。
Timer 也会有一些缺陷:
- Timer 是单线程的,假如有任务 A,B,C,任务 A 如果执行时间比较长,那么就会影响任务 B,C 的启动和执行时间,如果 B,C 执行时间也比较长,那就会相互影响;
- Timer 不会捕获异常,如果 A,B,C 任何一个任务在执行过程中发生异常,就会导致 TImer 整个定时任务停止工作;
- Timer 是基于绝对时间调度的,而不是基于相对时间,所以它对系统时间的改变非常敏感;
所以,如果在使用 Timer 的过程中要注意这些缺陷,虽然可以用,但不推荐。
3、ScheduledExecutorService
因 Timer 有一些缺陷,所以不太建议使用 Timer,推荐使用 ScheduledExecutorService:

ScheduledExecutorService 即是 Timer 的替代者,JDK 1.5 并发包引入,是基于线程池设计的定时任务类:
java.util.concurrent.Executors.newScheduledThreadPool

上了线程池,每个调度任务都会分配到线程池中的某一个线程去执行,任务就是并发调度执行的,任务之间互不影响。
几个重要的调度方法:

- schedule:只执行一次调度;
- scheduleAtFixedRate:按固定频率调度,如果执行时间过长,下一次调度会延迟,不会同时执行;
- scheduleWithFixedDelay:延迟调度,上一次执行完再加上延迟时间后执行;
另外,可以看出,任务是支持 Runnable 和 Callable 调度的。
实现代码如下:
/**
* 线程池定时任务
* 来源公众号:Java技术栈
*/
public static void poolTask(){
ScheduledExecutorService pool = Executors.newScheduledThreadPool(10);
pool.scheduleAtFixedRate(() -> {
System.out.println("hi, 欢迎关注:Java技术栈");
}, 2000, 3000, TimeUnit.MILLISECONDS);
}
这是一个按固定频率调度的任务,创建了 10 个核心线程数,首次执行延迟 2 秒,后续每 3 秒执行一次。
这种方式简单、好用,避免了使用 Timer 带来的各种问题,推荐使用这种实现方式。
总结
好了,本文栈长分享了 3 种 Java 实现定时任务的方式,也相对简单,但执行频率时间设置都太简单,只适合简单的业务,不适合实际复杂业务的需求,实际业务要考虑分布式、故障转移恢复等远要复杂的多。
本文仅给大家一个参考吧,在不用框架的前提下也能实现定时任务,在小而美的场景,还是很香的。
最后,Java 系列教程还会继续更新,关注Java技术栈公众号第一时间推送,还可以在公众号菜单中获取历史 Java 教程,都是干货。
本节教程所有实战源码已上传到这个仓库:
最后,觉得我的文章对你用收获的话,动动小手,给个在看、转发,原创不易,栈长需要你的鼓励。
版权申明:本文系公众号 "Java技术栈" 原创,原创实属不易,转载、引用本文内容请注明出处,禁止抄袭、洗稿,请自重,尊重他人劳动成果和知识产权。
不用任何框架,Java 就能实现定时任务的 3 种方法!的更多相关文章
- java实现定时任务的三种方法 - 转载
java实现定时任务的三种方法 /** * 普通thread * 这是最常见的,创建一个thread,然后让它在while循环里一直运行着, * 通过sleep方法来达到定时任务的效果.这样可以快速简 ...
- Java中获取键盘输入值的三种方法
Java中获取键盘输入值的三种方法 Java程序开发过程中,需要从键盘获取输入值是常有的事,但Java它偏偏就没有像c语言给我们提供的scanf(),C++给我们提供的cin()获取键盘输入值 ...
- java 获取键盘输入常用的两种方法
java 获取键盘输入常用的两种方法 方法1: 通过 Scanner Scanner input = new Scanner(System.in); String s = input.nextLine ...
- Java 8创建Stream流的5种方法
不知不觉间,Java已经发展到13了,来不及感慨时间过得真的太快了,来不及学习日新月异的技术更新,目前大多数公司还是使用的JDK8版本,一方面是版本的稳定,另一方面是熟悉,所以很多公司都觉得不升级也挺 ...
- PostgreSQL 实现定时任务的 4 种方法
数据库定时任务可以用于实现定期的备份.统计信息采集.数据汇总.数据清理与优化等.PostgreSQL 没有提供类似 Oracle.MySQL 以及 Microsoft SQL Sever 的内置任务调 ...
- openstack中运行定时任务的两种方法及源代码分析
启动一个进程,如要想要这个进程的某个方法定时得进行执行的话,在openstack有两种方式: 一种是通过继承 periodic_task.PeriodicTasks,另一种是使用loopingcall ...
- Java入门:Java中获取键盘输入值的三种方法
Java程序开发过程中,需要从键盘获取输入值是常有的事,但Java它偏偏就没有像c语言给我们提供的scanf(),C++给我们提供的cin()获取键盘输入值的现成函数!Java没有提供这样的函数也不代 ...
- java四舍五入保留两位小数4种方法
4种方法,都是四舍五入,例: import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.NumberF ...
- Java中实现线程同步的三种方法
实现同步的三种方法 多线程共享数据时,会发生线程不安全的情况,多线程共享数据必须同步. 实现同步的三种方法: 使用同步代码块 使用同步方法 使用互斥锁ReetrantLock(更灵活的代码控制) 代码 ...
随机推荐
- 记一次亲身体验的勒索病毒事件 StopV2勒索病毒
昨天给笔记本装了 windows server 2016 操作系统,配置的差不多之后,想使用注册机激活系统.使用了几个本地以前下载的注册机激活失败后,尝试上网搜索. 于是找到下面这个网站(这个网站下载 ...
- ASP.NET 部署IIS后如何访问共享目录文件
1.我的电脑-->管理-->系统工具-->本地用户和组-->用户-->右键新建用户-->创建一个与远程文件夹相同的账号密码! 如下图: 以上为部署接口服务器中的用户 ...
- 009.NET5_程序的发布运行
发布 相差了web.config文件 脚本启动 cmd,进入程序根目录. 带参启动 其实,最终与web.config中效果一样
- webpack4.0源码解析之esModule打包分析
入口文件index.js采用esModule方式导入模块文件,非入口文件index1.js分别采用CommonJS和esmodule规范进行导出. 首先,init之后创建一个简单的webpack基本的 ...
- js array flat all in one
js array flat all in one array flat flatMap flatMap > flat + map https://developer.mozilla.org/en ...
- Deno 1.0 & Node.js
Deno 1.0 & Node.js A secure runtime for JavaScript and TypeScript. https://deno.land/v1 https:// ...
- background & background-image & border-image
background & background-image & border-image https://developer.mozilla.org/en-US/docs/Web/CS ...
- overwrite & override
overwrite & override explanation https://stackoverflow.com/questions/8651562/overwrite-or-overri ...
- moment.js & convert timestamps to date string in js
moment.js & convert timestamps to date string in js https://momentjs.com/ moment().format('YYYY- ...
- go-admin在线开发平台学习-4[细节解析]
紧接着上一期的内容,继续对go-admin的一些细节进行解析. 通用的增删改查方法 在go-admin/common/actions下封装了通用的增删改查方法.在对这些方法进行说明前,先移步到另一个文 ...