今天我们不用任何框架,用最朴素的 Java API 来实现定时任务,本文会介绍 3 种实现方案,我们一起来看...

1、 sleep

这也是我们最常用的 sleep 休眠大法,不只是当作休眠用,我们还可以利用它很轻松的能实现一个简单的定时任务。

实现逻辑:

新开一个线程,添加一个 for/ while 死循环,然后在死循环里面添加一个 sleep 休眠逻辑,让程序每隔 N 秒休眠再执行一次,这样就达到了一个简单定时任务的效果。

实现代码如下:

/*** 休眠实现定时任务*/
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 表达式。

2、Timer

来看下 JDK 自带的 java.util.Timer 类:

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

几个重要的方法:

  • schedule:开始调度任务,提供了几个包装方法;

  • cancle:终止任务调度,取消当前调度的所有任务,正在运行的任务不受影响;

  • purge:从任务队列中移除所有已取消的任务;

另外,java.util.TimerTask 就是实现了 Runnable 接口,具体任务逻辑则是在 run 方法里去实现。

实现代码如下:

/*** timer定时任务*/
private static void timerTask() throws InterruptedException {Timer timer = new Timer();TimerTask timerTask = new TimerTask() {@Overridepublic void run() {System.out.println("hi, 欢迎关注");}};// 第一次任务延迟时间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 调度的。

实现代码如下:

/*** 线程池定时任务*/
public static void poolTask(){ScheduledExecutorService pool = Executors.newScheduledThreadPool(10);pool.scheduleAtFixedRate(() -> {System.out.println("hi, 欢迎关注");}, 2000, 3000, TimeUnit.MILLISECONDS);
}

这是一个按固定频率调度的任务,创建了 10 个核心线程数,首次执行延迟 2 秒,后续每 3 秒执行一次。

这种方式简单、好用,避免了使用 Timer 带来的各种问题,推荐使用这种实现方式。

总结

本文分享了 3 种  Java 实现定时任务的方式,也相对简单,但执行频率时间设置都太简单,只适合简单的业务,不适合实际复杂业务的需求,实际业务要考虑分布式、故障转移恢复等远要复杂的多。

本文仅给大家一个参考吧,在不用框架的前提下也能实现定时任务,在小而美的场景,还是很香的。


往期推荐

定时任务的实现原理,看完就能手撸一个!

一口气说出 6 种延时队列的实现方法,面试官满意的笑了

JDK 9 对字符串 String 的优化,挺有意思!

Java中实现定时任务的3种方法!相关推荐

  1. Java中创建数组的几种方法

    Java中创建数组的几种方法 public static void main(String[] args) { //创建数组的第一种方法 int[] arr=new int[6]; int intVa ...

  2. 在 Java 中初始化 List 的五种方法

    转载自  在 Java 中初始化 List 的五种方法 Java 中经常需要使用到 List,下面简单介绍几种常见的初始化方式. 1.构造 List 后使用 List.add 初始化 List< ...

  3. 高级 | Java中获取类名的3种方法

    转载自 高级 | Java中获取类名的3种方法 获取类名的方法 Java 中获取类名的方式主要有以下三种. getName() 返回的是虚拟机里面的class的类名表现形式. getCanonical ...

  4. Java中String字符串截取几种方法(substring,split)

    Java中String字符串截取几种方法 substring,split 这是一个Java中的String的基础用法的演示. 下面通过代码对大家进行讲解 substring 这里用来ndexOf,la ...

  5. 谈谈java中遍历Map的几种方法

    java中的map遍历有多种方法,从最早的Iterator,到java5支持的foreach,再到java8 Lambda,让我们一起来看下具体的用法以及各自的优缺点 先初始化一个map public ...

  6. Java 中 List 分片的 5 种方法!

    作者 | 王磊 来源 | Java中文社群(ID:javacn666) 转载请联系授权(微信ID:GG_Stone) 前些天在实现 MyBatis 批量插入时遇到了一个问题,当批量插入的数据量比较大时 ...

  7. JAVA中文件写入的6种方法

    在 Java 中操作文件的方法本质上只有两种:字符流和字节流,而字节流和字符流的实现类又有很多,因此在文件写入时我们就可以选择各种各样的类来实现.我们本文就来盘点一下这些方法,顺便测试一下它们性能,以 ...

  8. java中遍历HashMap的四种方法及效率比较

    1. 推荐方法: 使用entrySet 遍历Map 类集合KV,而不是keySet 方式进行遍历. 代码示例如下: // 循环第二种HashMap<Integer, String> map ...

  9. java中遍历map的几种方法介绍

          喜欢用Java写程序的朋友都知道,我们常用的一种数据结构map中存储的是键值对,我们一般存储的方式是: map.put(key, value); 而提取相应键的值用的方法是: map.ge ...

最新文章

  1. 计算机在医疗方面的应用与前景,计算机在医疗卫生领域应用现状及前景.docx
  2. mini2440系统引导(四)存储控制器
  3. Android怎样保证一个线程最多仅仅能有一个Looper?
  4. devi into python 笔记(一)字典 列表的简单操作
  5. 伪类、伪元素、锚伪类
  6. 超可爱桌面电子宠物下载
  7. ubuntu ssh服务器与客户端的文件互传
  8. [C++]二维数组还是一维数组?
  9. 互联网日报 | 4月29日 星期四 | 虎牙20亿购买LPL直播版权;返利网正式借壳登陆A股;淘宝直播全面开放官方货品池
  10. C语言中 malloc函数介绍
  11. MATLAB读取图片时报错:“错误使用 fopen 找不到文件,确保文件存在且路径” 的原因及解决方法
  12. 6-2 Pyecharts配置项及在ppt中插入pyecharts图表
  13. TcaplusDB君 · 行业新闻汇编(五)
  14. clickhouse 新增列_# 记录一次clickhouse表新增字段遇到的问题
  15. 电子警察系统设计(原理+流程+论文)
  16. html网站运行天数代码,网站运行天数代码
  17. oVirt管理端高可用模式搭建(hosted-engine)
  18. 错题本——Python
  19. 按键精灵大漠插件使用基础练习入门代码
  20. iOS 使用nib文件(一)

热门文章

  1. Docker容器间网络通信
  2. JAVA知识基础(六):抽象
  3. Ubuntu18.04安装cuDNN和Tensorflow的正确姿势
  4. Google File System 学习笔记
  5. 云场景实践研究第27期:袋鼠云
  6. 分布式系统的唯一ID
  7. (转) Twisted :第十九部分 改变之前的想法
  8. FreeMarker笔记 前言第1章 入门
  9. 【工作记录】android手势事件操作记录
  10. 机器翻译软件从实验室走向市场