JavaSE实现任务调度的三种方式(十六)

勿以恶小而为之,勿以善小而不为--------------------------刘备

劝诸君,多行善事积福报,莫作恶

上一章简单介绍了 JavaSE实现国际化操作(十五),如果没有看过,请观看上一章

一. 为什么要出现定时任务?

在实际生活和开发中,常常会遇到类似的情况, 如 1.女朋友生日那一天,发送定时消息,提醒生日快乐 2. 给某人写邮件编写好内容之后,在晚上10点时准时发送, 3 某天固定8点起床,晚上10点睡觉 4.母亲节,父亲节时给父母发送消息。 等生活中的定时小例子 (闹钟,备忘录都属于定时)。

开发中,1. 给已经注册的用户,在用户生日那天,发送生日祝福. 2. 公司员工,当入职满一年时,增加年假至5天,入职5年时,增加年假至10天。 3.定时删除三个月前的日志记录消息。 4. 定时监测网站访问情况等开发中的定时小例子。

这些生活和开发实例,都属于定时任务。 定时任务,是很常见的,也是很重要的。

JavaSE 阶段提供了 三种方式,来实现定时任务。

JavaEE 阶段,使用 Quartz 框架来实现定时任务。

二. JavaSE 阶段实现定时任务

二.一 多线程 Runnable 和Thread 实现定时任务

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 
 * Thread 多线程通过休眠时间来实现定时执行
 * @author 两个蝴蝶飞
 *
 */
public class ThreadDemo {
	public static void main(String []args){
		//定时的时间
		Long SLEEP_TIME=1000*5L;
		Runnable runnable=new Runnable() {
			@Override
			public void run() {
				while(true){
					try {
						//通过设置休眠,来达到定时的效果。 每5s执行一次 
						Thread.sleep(SLEEP_TIME);
						SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
						String localeDate=sdf.format(new Date());
						System.out.println("Thread 线程:当前时间为:"+localeDate);
						
					} catch (InterruptedException e) {
						// TODO 自动生成的 catch 块
						e.printStackTrace();
					}
				}
				
			}
		};
		
		Thread thread=new Thread(runnable);
		thread.start();
		
	}
}

控制台打印输出:

有图片

如果想取消定时的话, 可以定义一个全局常量flag,flag默认是true, 将 while(true) 改成 while(flag), 当取消定时时,将flag 由true 改成false 即可。

二.二 TimerTask 和 Timer 实现定时任务

二.二.一 代码编写

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/**
 * 
 * TimerTask 和Timer 实现定时任务
 * @author 两个蝴蝶飞
 *
 */
public class TaskDemo {
	public static void main(String[] args) {
		Timer timer=new Timer();
		TimerTask task=new TimerTask() {	
			@Override
			public void run() {
				SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				String localeDate=sdf.format(new Date());
				System.out.println("Task任务:当前时间为:"+localeDate);
			}
		};
		timer.scheduleAtFixedRate(task,0,5000);
		
		//调用  cancel()方法来取消定时
		//timer.cancel();
	}
}

通过调用 Timer 对象的 scheduleAtFixedRate() 方法,来实现定时任务。

控制台打印输出:

有图片

二.二.二 Timer中的调度方法

task 均为执行定时任务的对象,时间单位为 ms.

方法签名 作用
schedule(TimerTask task, long delay) 在delay ms 后执行定时任务,只执行一次
schedule(TimerTask task, Date time) 在time 时间时执行定时任务,只执行一次
public void schedule(TimerTask task, long delay, long period) 在delay ms后执行定时任务,
并且每隔 period 毫秒再次执行任务,会多次执行,
以固定时间
schedule(TimerTask task, Date firstTime, long period) 在firstTime时间执行定时任务,
并且每隔 period毫秒再次执行任务,会多次执行,
以固定时间
scheduleAtFixedRate(TimerTask task, long delay, long period) 在delay ms后执行定时任务,
并且每隔 period 毫秒再次执行任务,会多次执行,
不一定均以固定时间执行
scheduleAtFixedRate(TimerTask task, Date firstTime, long period) 在firstTime时间执行定时任务,
并且每隔 period毫秒再次执行任务,会多次执行,
不一定均以固定时间执行

schedule()方法和 scheduleAtFixedRate()方法,是有一些区别的。

schedule()方法,假设是每5s执行一次,由5s,10s,15s,20s时执行, 但由于CPU征用导致定时任务并没有在5s这个时间上执行,而是在9s时执行了,延迟了4s,那么下一次该任务执行的时间是 9+5=14s,而不是原计划的10s. 这样会导致可能漏掉调度的情况。

scheduleAtFixedRate()方法,假设是每5s执行一次,由5s,10s,15s,20s时执行, 但由于CPU征用导致定时任务并没有在5s这个时间上执行,而是在9s时执行了,延迟了4s,那么下一次该任务执行的时间还是10s, 与原计划一样(会将间隔时间的period=5000 变成period=1000,但下下一次仍然是5000), 不会导致漏掉调度的情况。

故常常使用的是 scheduleAtFixedRate() 方法。

二.三 ScheduledExecutor 实现定时任务

在 java.util.concurrent 包下

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
 * ScheduledExecutor 实现定时调度
 * @author 两个蝴蝶飞
 *
 */
public class ScheduleDemo {
	public static void main(String[] args) {
		//定义定时任务
		Runnable runnable=new Runnable() {
			@Override
			public void run() {
				SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				String localeDate=sdf.format(new Date());
				System.out.println("Scheduled调度:当前时间为:"+localeDate);
			}
		};
		//定义调度服务
		ScheduledExecutorService scheduledExecutorService=Executors.newSingleThreadScheduledExecutor();
		//运行, 哪个任务,开始时间,间隔时间,间隔单位
		scheduledExecutorService.scheduleAtFixedRate(runnable, 0, 2, TimeUnit.SECONDS);
	}
}

控制台打印输出:

有图片

三. JavaSE 实现调度的不足

上面的三种方式,虽然都实现了定时任务,但面对复杂多变的定时任务情况,就显得力不从心了。

如,

  1. 无法实现类似 ‘每年的母亲节给母亲发送祝福’,‘每个月的最后一天显示员工的本月的工作情况’,‘距离每个月10号最近的工作日发薪资’ 等非固定日期的情况。开发者不能在程序中进行判断日期。

  2. 无法与当前主流框架 Spring 进行很好的整合。

  3. 定时任务的事务处理,需要开发者自己进行处理。

  4. 定时任务持久化保存

想处理好定时任务,可以学习一下 Quartz 定时任务框架。


谢谢您的观看!!!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章