Java 定时调度Timer&Quartz
目录
二、Timer
2、schedule
三、Quartz
一、Java定时任务介绍
在Java中,用得比较多的有两种,一个是Timer,一个是Quartz;
其中Timer是这是jdk自带的类库,一般用来实现简单的定时调度,由一个后台线程进行任务的调度,所以对于并发调度不友好;
Quartz不是jdk自带的,但是他的功能更加强大,一般用于比较复杂的定时调度,可以解决Timer的并发调度问题;
二、Timer
2.1、Timer与TimerTask
Timer是在jdk自带的工具类,Timer为与java.util包下,可以将其理解为一个“定时器”;
当定时器到点后,执行的任务是TimerTask,称为“任务”;
下面就是一个TimerTask示例,注意需要重写TimerTask的run方法,类似于Runnable接口的run方法。
package cn.ganlixin.task; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimerTask; public class MyTask extends TimerTask { @Override public void run() { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println(dateFormat.format(new Date())); } }
下面是一个简单的定时执行任务的示例:
package cn.ganlixin.timer; import cn.ganlixin.task.MyTask; import org.junit.Test; import java.text.ParseException; import java.util.Timer; public class TestTimer { @Test public void testTimerDemo() throws InterruptedException { // 创建一个定时器 Timer timer = new Timer(); // 5秒之后执行任务,只执行一次,执行MyTask的run方法 // schedule(TimerTask task, long delay) timer.schedule(new MyTask(), 5 * 1000L); // 让当前线程阻塞,等待上面的任务执行 Thread.sleep(10000); } }
Timer有几个可以设置任务执行的方法:
// 在deley毫秒毫秒后,开始执行task,只执行一次 void schedule(TimerTask task, long delay) // 在deley毫秒毫秒后,开始执行task,之后每隔period毫秒执行一次task void schedule(TimerTask task, long delay, long period) // 在date时刻,开始执行task,只执行一次 void schedule(TimerTask task, Date date) // 在date时刻,开始执行task,之后每隔period毫秒执行一次task void schedule(TimerTask task, Date firstTime, long period) // 和schedule(TimerTask task, long delay, long period)一样,但是以固定速率执行 scheduleAtFixedRate(TimerTask task, long delay, long period) // 和schedule(TimerTask task, Date firstTime, long period)一样,但是以固定速率执行 scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
schedule和scheduleAtFixRate有一些区别。
2.2、schedule
1、当首次计划执行时间早于当前时间,比如要求在1980年1月1日凌晨执行某个任务
对于这种情况,启动定时任务后,会立即执行任务。下面是例子:
package cn.ganlixin.timer; import org.junit.Test; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TestTimer { @Test public void testStartBefore() throws InterruptedException, ParseException { Timer timer = new Timer(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("当前时间:" + dateFormat.format(new Date())); Date date = dateFormat.parse("2019-01-01 01:00:00"); // 开始时间为过去 timer.schedule(new TimerTask() { @Override public void run() { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date scheduledExecuteTime = new Date(scheduledExecutionTime()); System.out.println("预计执行任务时间:" + dateFormat.format(scheduledExecuteTime)); System.out.println("开始任务:" + dateFormat.format(new Date())); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("完成任务:" + dateFormat.format(new Date())); } }, date); // 让当前线程阻塞,等待上面的任务执行 Thread.sleep(100000); } }
运行输出:
当前时间:2019-03-07 07:56:47 预计执行任务时间:2019-01-01 01:00:00 开始任务:2019-03-07 07:56:47 完成任务:2019-03-07 07:56:57
2、首次执行时间在当前时间之后,但是执行时间超过了period(周期)
比如,有一个任务在4秒后启动,每3秒执行1次,任务每次执行要10秒,此时,因为执行任务所需时间超过了周期时间,所以一次任务执行完后,立即执行下一次任务;
package cn.ganlixin.timer; import org.junit.Test; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TestTimer { @Test public void testTimerDemo() throws InterruptedException, ParseException { // 创建一个定时器 Timer timer = new Timer(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("当前时间:" + dateFormat.format(new Date())); timer.schedule(new TimerTask() { @Override public void run() { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date scheduledExecuteTime = new Date(scheduledExecutionTime()); System.out.println("预计执行任务时间:" + dateFormat.format(scheduledExecuteTime)); System.out.println("开始任务:" + dateFormat.format(new Date())); try { Thread.sleep(10000); // 休眠10秒,模拟任务执行耗时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("完成任务:" + dateFormat.format(new Date())); System.out.println("-----------------------------------------"); } }, 4 * 1000L, 3 * 1000L); // 让当前线程阻塞,等待上面的任务执行 Thread.sleep(100000); } }
运行结果:
当前时间:2019-03-07 08:14:40 预计执行任务时间:2019-03-07 08:14:44 开始任务:2019-03-07 08:14:44 完成任务:2019-03-07 08:14:54 ----------------------------------------- 预计执行任务时间:2019-03-07 08:14:54 开始任务:2019-03-07 08:14:54 完成任务:2019-03-07 08:15:04 ----------------------------------------- 预计执行任务时间:2019-03-07 08:15:04 开始任务:2019-03-07 08:15:04 完成任务:2019-03-07 08:15:14 ----------------------------------------- 预计执行任务时间:2019-03-07 08:15:14 开始任务:2019-03-07 08:15:14 .........
2.3、scheduleAtFixRate
1、当首次计划执行时间早于当前时间,那么scheduleAtFixRate会将错过的这段时间中,本应该执行的任务“补”回来。
package cn.ganlixin.timer; import org.junit.Test; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TestTimer { @Test public void testTimerDemo() throws InterruptedException, ParseException { // 创建一个定时器 Timer timer = new Timer(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("当前时间:" + dateFormat.format(new Date())); Date date = dateFormat.parse("2019-01-01 01:00:00"); // 开始时间为过去 timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date scheduledExecuteTime = new Date(scheduledExecutionTime()); System.out.println("预计执行任务时间:" + dateFormat.format(scheduledExecuteTime)); System.out.println("开始任务:" + dateFormat.format(new Date())); try { Thread.sleep(10000); // 休眠10秒,模拟任务执行耗时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("完成任务:" + dateFormat.format(new Date())); System.out.println("-----------------------------------------"); } }, date, 3 * 1000L); // 让当前线程阻塞,等待上面的任务执行 Thread.sleep(100000); } }
2、首次执行时间在当前时间之后,但是执行时间超过了period(周期),此时会将错过的任务补回来
package cn.ganlixin.timer; import org.junit.Test; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TestTimer { @Test public void testTimerDemo() throws InterruptedException, ParseException { // 创建一个定时器 Timer timer = new Timer(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("当前时间:" + dateFormat.format(new Date())); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date scheduledExecuteTime = new Date(scheduledExecutionTime()); System.out.println("预计执行任务时间:" + dateFormat.format(scheduledExecuteTime)); System.out.println("开始任务:" + dateFormat.format(new Date())); try { Thread.sleep(10000); // 休眠10秒,模拟任务执行耗时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("完成任务:" + dateFormat.format(new Date())); System.out.println("-----------------------------------------"); } }, 4 * 1000L, 3 * 1000L); // 让当前线程阻塞,等待上面的任务执行 Thread.sleep(100000); } }
运行输出
当前时间:2019-03-07 08:26:53 预计执行任务时间:2019-03-07 08:26:57 开始任务:2019-03-07 08:26:57 完成任务:2019-03-07 08:27:07 ----------------------------------------- 预计执行任务时间:2019-03-07 08:27:00 开始任务:2019-03-07 08:27:07 完成任务:2019-03-07 08:27:17 ----------------------------------------- 预计执行任务时间:2019-03-07 08:27:03 开始任务:2019-03-07 08:27:17 完成任务:2019-03-07 08:27:27 ----------------------------------------- 预计执行任务时间:2019-03-07 08:27:06 开始任务:2019-03-07 08:27:27 完成任务:2019-03-07 08:27:37
三、Quartz