定时任务简单来说就是在指定时间,指定的频率来执行一个方法,而在 Java 中我们又该如何实现呢?

想来主要有 3 种方式,最原始的方式肯定是开启一个线程,让它睡一会跑一次睡一会跑一次这也就达到了定频率的执行 run 方法,我们只需要将业务逻辑写在 run 方法中即可。这种方式总结就是单个线程来执行单个任务。

方式一:创建一个线程

package com.yu.task;

import java.util.Date;

public class ThreadTest {

    public static void main(String[] args) {
// 设置执行周期
final long timeInterval = 3000; Runnable runnable = new Runnable() {
public void run() {
while (true) {
System.out.println("Task Run ... " + new Date()); try {
Thread.sleep(timeInterval);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
}

第二种方式:使用 JDK 自带的 API Timer 以及 TaskTimer。

这种方式和第一种简单粗暴的方式有什么区别呢,主要体现在使用 API 可以在指定的时间开始启动任务,可以延期执行首次任务,同样也看可以设置一定的时间间隔,但是原理是是一样的,后台还是启动了一个线程,应该说是只有一个线程在执行任务,不管我们启动的 Task 有几个。所以这也会有问题,比方说一个一个任务没有执行完成,另一个任务就开始执行了,可能会发生并发问题。还有若是一个任务中报错,则线程就会被停止。

package com.yu.task;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask; public class MyTask extends TimerTask{ private String name; public MyTask(String name) {
this.name = name;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public void run() {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = sf.format(new Date());
System.out.println("exec MyTask ... 当前时间为:" + format);
System.out.println(this.name +" 正在执行!" + sf.format(new Date()));
} public static void main(String[] args) {
Timer timer = new Timer();
TimerTask task1 = new MyTask("Tasks 1");
TimerTask task2 = new MyTask("Tasks 2"); Calendar calendar1 = Calendar.getInstance();
calendar1.add(Calendar.SECOND, 3);
Calendar calendar2 = Calendar.getInstance();
calendar2.add(Calendar.SECOND, 5); SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = sf.format(new Date());
System.out.println("当前时间为:" + format); timer.schedule(task1, calendar1.getTime(), 3000L);
timer.schedule(task2, calendar2.getTime(), 3000L);
} }

其实在 Timer 中,封装了一个 Task 的队列和 Time 的线程对象,我们自定义的 Task 的引用会放在队列中等待执行。

大致是这么一个关系 Timer -  TimerThread - TaskQueue - MyTask - run  当然最终执行的方法肯定是我们自定义任务中的 run 方法。因为我们自定义的任务已经继承了 TimeTask ,而这个类已经实现了 Runnable 接口。

Timer 定时器第一种方式好的地方还在于可以选择关闭任务,查看任务的执行情况等。下面介绍几个相关的方法。

启动定时任务也有几个不同的方法,每个都有不同的使用场景。

上面是 Timer 类中的属性和方法的简图,开启一个任务我们主要使用 6 个方法, 一共 3 组,先看简单的

schedule() 方法中含有两个参数,主要是用来执行一次任务的,不存在频率的问题,而 3 个参数表示执行什么任务,什么时候开始执行/延时多久执行,多久执行一次。

现在来假设一个场景,01秒开始执行一个任务,频率是 3 秒执行一次,不知道你们有没有想到这个情况,若是这个方法本身执行需要 4 秒,那第二次执行的时间是 04 呢? 还是 05 秒呢?

而这个差别就是 schedule 和 scheduleAtFixedRate 方法的区别,前者会按照任务执行的情况来执行下一次任务,也就是说 01 之后,会等第一次执行结束再开始第二次,这样就会带来一个问题,每次执行时间都比预想的要晚。而 scheduleAtFixedRate 则不存在这个问题,它会按照指定的时间和频率执行,这样坐就会出现,同一时刻会有两个任务在同时执行,若是,可能会发生并发问题。

上面讨论的是当任务本身执行的时间大于频率时,两个方法不同的执行情况,还有一个情况,若是当前时间晚于我们设定的开始执行时间又会怎么办呢?

schedule () 会比较符合正常思维,晚了就晚了,现在开始执行就是,不存在补回的情况,但是 scheduleAtFixedRate 则会一次性补回未执行的次数。举例来说,当前时间为 09,而我们设定的开始时间为 00 ,频率为 3 秒,则 schedule 会在 09 执行一次,12 执行一次。而 scheduleAtFixedRate 会在 09 一次性执行 4 次,12 执行一次,后面就是正常的频率。

下面再说几个可能会起到锦上添花作用的方法,我们的若是想取消任务的执行,有一个方法但是分为两个类执行,我们可以调用 TimeTask 中的 cancel 方法,这个方法只对当前任务有效,若是想取消全部的任务,则需要调用 Timer 中的 cancel 方法。

那有该如何查看定时器 Timer 中已经被取消任务的数量呢?当然还是有方法的,那就是 Timer 中的 purge 方法。

说了这么说,实际上我们可以看到,Timer 定时器本身还是调用线程来完成定时操作。且后台只有一个多线程 TimeThread 在工作。

那 Timer 定时器有什么缺点呢?

1 并发操作时的缺陷,这是因为 Timer 的后台只有一个执行线程导致的,容易引起并发问题。

2 任务抛出异常时缺陷。如果 TimeTask 抛出 RuntimeException,Timer 会停止所有任务的执行。

所以以后我们在使用 Timer 定时器的时候要注意,这两个情况,多任务且并发执行的时候不要使用 Timer,复杂任务调度的时候也不要使用 Timer ,因为一个不小心出现异常了,所有任务都卡壳了。但是,Timer 定时器处理一些简单的定是任务还是非常方便的!比方说我想实现的,定时发送邮件。用起来就很是方便,因为这是 JDK 自带的 API 呀!

第三种方式:使用 Java 中的专门用于定时任务管理的框架 Quartz 。(还没学,等等吧……)

上面也说了 Timer 定时器的弊端,怎么办,据听说 Quartz 可以解决这些……

后记:据我知道,Java 中定时任务也就这几个吧,欢迎补充,有好多人说到某某框架中有定时任务,我想说的是,那不是!那是框架集成了上面说的定时任务框架,可能集成的就是 Quartz,或是 Quartz 的简化版本……

Java 中的定时任务(一)的更多相关文章

  1. 转:java中的定时任务

    引自:http://www.cnblogs.com/wenbronk/p/6433178.html java中的定时任务, 使用java实现有3种方式: 1, 使用普通thread实现 @Test p ...

  2. Java中的定时任务

    现代的应用程序早已不是以前的那些由简单的增删改查拼凑而成的程序了,高复杂性早已是标配,而任务的定时调度与执行也是对程序的基本要求了. 很多业务需求的实现都离不开定时任务,例如,每月一号,移动将清空你上 ...

  3. java中实现定时任务 task 或quartz

    转载大神的 https://www.cnblogs.com/hafiz/p/6159106.html https://www.cnblogs.com/luchangyou/p/6856725.html ...

  4. java中的定时任务小示例

    package package_1; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; ...

  5. java中实现定时功能

    网上资料: 我们可以使用Timer和TimerTask类在java中实现定时任务,详细说明如下: 1.基础知识java.util.Timer一种线程设施,用于安排以后在后台线程中执行的任务.可安排任务 ...

  6. java中基于TaskEngine类封装实现定时任务

    主要包括如下几个类: 文章标题:java中基于TaskEngine类封装实现定时任务 文章地址: http://blog.csdn.net/5iasp/article/details/10950529 ...

  7. 在Java Web项目中添加定时任务

    在Java Web程序中加入定时任务,这里介绍两种方式:1.使用监听器注入:2.使用Spring注解@Scheduled注入. 推荐使用第二种形式. 一.使用监听器注入 ①:创建监听器类: impor ...

  8. 如何用 Java 实现 Web 应用中的定时任务?

    定时任务,是指定一个未来的时间范围执行一定任务的功能.在当前WEB应用中,多数应用都具备任务调度功能,针对不同的语音,不同的操作系统, 都有其自己的语法及解决方案,windows操作系统把它叫做任务计 ...

  9. 如何用 Java 实现 Web 应用中的定时任务

    定时任务,是指定一个未来的时间范围执行一定任务的功能.在当前WEB应用中,多数应用都具备任务调度功能,针对不同的语音,不同的操作系统, 都有其自己的语法及解决方案,windows操作系统把它叫做任务计 ...

随机推荐

  1. JS回调函数的应用,原来这么简单!

    JS的回调函数很简单,看代码: 在a.js中 var myback = null; function load(obj){ myback = obj; } function save(){ // 后台 ...

  2. 【leetcode 简单】 第一百五十题 两个列表的最小索引总和

    假设Andy和Doris想在晚餐时选择一家餐厅,并且他们都有一个表示最喜爱餐厅的列表,每个餐厅的名字用字符串表示. 你需要帮助他们用最少的索引和找出他们共同喜爱的餐厅. 如果答案不止一个,则输出所有答 ...

  3. Hive笔记之collect_list/collect_set(列转行)

    Hive中collect相关的函数有collect_list和collect_set. 它们都是将分组中的某列转为一个数组返回,不同的是collect_list不去重而collect_set去重. 做 ...

  4. Sort Colors I & II

    Given an array with n objects colored red, white or blue, sort them so that objects of the same colo ...

  5. jQuery插件之ajaxFileUpload(异步上传图片并实时显示,并解决onchange后ajaxFileUpload失效问题)

    参考学习: 第一篇:http://www.cnblogs.com/kissdodog/archive/2012/12/15/2819025.html 第二篇:http://www.jb51.net/a ...

  6. hibernate的枚举注解@Enumerated

    @Enumerated(value=EnumType.ORDINAL)采用枚举类型的序号值与数据库进行交互, 此时数据库的数据类型需要是数值类型,例如在实际操作中 CatTest ct = new C ...

  7. Hash 分布均衡算法

    1.移位实现 public static int GetIndex(string str, int count) { , (current, c) => (current << ) ...

  8. python网络编程--RabbitMQ

    一:RabbitMQ介绍 RabbitMQ是AMPQ(高级消息协议队列)的标准实现.也就是说是一种消息队列. 二:RabbitMQ和线程进程queue区别 线程queue:不能跨进程,只能用于多个线程 ...

  9. android设备休眠

    从上面的连接里面找到了一些资料: 如果一开始就对Android手机的硬件架构有一定的了解,设计出的应用程序通常不会成为待机电池杀手,而要设计出正确的通信机制与通信协议也并不困难.但如果不去了解而盲目设 ...

  10. P2184 【贪婪大陆】

    看到全是线段树或者树状数组写法,就来提供一发全网唯一cdq分治三维偏序解法吧 容易发现,这个题的查询就是对于每个区间l,r,查询有多少个修改区间li,ri与l,r有交集 转化为数学语言,就是查询满足l ...