加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Java实现Web应用中的定时任务(实例讲解)

发布时间:2020-05-23 15:55:50 所属栏目:Java 来源:互联网
导读:定时任务,是指定一个未来的时间范围执行一定任务的功能。在当前WEB应用中,多数应用都具备任务调度功能,针对不同的语音,不同的操作系统,都有其自己的语法及解决方案,windows操作系统把它叫做任务计划,linux中c

定时任务,是指定一个未来的时间范围执行一定任务的功能。在当前WEB应用中,多数应用都具备任务调度功能,针对不同的语音,不同的操作系统, 都有其自己的语法及解决方案,windows操作系统把它叫做任务计划,linux中cron服务都提供了这个功能,在我们开发业务系统中很多时候会涉及到这个功能。本场chat将使用java语言完成日常开发工作中常用定时任务的使用,希望给大家工作及学习带来帮助。

一、定时任务场景

(1)驱动处理工作流程

作为一个新的预支付订单被初始化放置,如果该订单在指定时间内未进行支付,则将被认为超时订单进行关闭处理;电商系统中应用较多,用户购买商品产生订单,但未进行支付,订单产生30分钟内未支付将关闭订单(且满足该场景数量庞大),不可能采用人工干预。

(2)系统维护

调度工作将获取系统异常日志,及某些关键点数据存储到数据库中,每个工作日(节假日除外平日)在11:30 PM转储到数据库,且生成一个XML文件发送至某位员工邮箱。

(3)在应用程序内提供提醒服务。

系统定时提醒登录用户某时间点执行相关工作。

(4)定时对账任务

公司与三方公司(运营商,银行等)业务,每天零点后进行当天业务的对账,将对账信息结果数据发送至相关负责人邮箱,第二天工作时间进行处理不匹配数据。

(5)数据统计

数据记录较多,实时从数据库读取查询会产生一定时间,为客户体验及性能需要,故每周(天,小时)将数据进行汇总,从而在展示数据时能够快速的呈现数据。

使用定时任务的场景还有很多... 看来定时任务在我们日常的开发中真的应用很广泛...

二、主流定时任务技术讲解 Timer

相信大家都已经非常熟悉 java.util.Timer 了,它是最简单的一种实现任务调度的方法,下面给出一个具体的例子:

package com.ibm.scheduler; 
 import java.util.Timer; 
 import java.util.TimerTask; 

 public class TimerTest extends TimerTask { 

 private String jobName = ""; 

 public TimerTest(String jobName) { 
  super(); 
  this.jobName = jobName; 
 } 

 @Override 
 public void run() { 
 System.out.println("execute " + jobName); 
 } 

 public static void main(String[] args) { 
  Timer timer = new Timer(); 
  long delay1 = 1 * 1000; 
  long period1 = 1000; 
  // 从现在开始 1 秒钟之后,每隔 1 秒钟执行一次 job1 
  timer.schedule(new TimerTest("job1"),delay1,period1); 
  long delay2 = 2 * 1000; 
  long period2 = 2000; 
  // 从现在开始 2 秒钟之后,每隔 2 秒钟执行一次 job2 
  timer.schedule(new TimerTest("job2"),delay2,period2); 
  } 
 }

/**
输出结果:
execute job1
execute job1
execute job2
execute job1
execute job1
execute job2
*/

使用 Timer 实现任务调度的核心类是 Timer 和 TimerTask。其中 Timer 负责设定 TimerTask 的起始与间隔执行时间。使用者只需要创建一个 TimerTask 的继承类,实现自己的 run 方法,然后将其丢给 Timer 去执行即可。Timer 的设计核心是一个 TaskList 和一个 TaskThread。Timer 将接收到的任务丢到自己的 TaskList 中,TaskList 按照 Task 的最初执行时间进行排序。TimerThread 在创建 Timer 时会启动成为一个守护线程。这个线程会轮询所有任务,找到一个最近要执行的任务,然后休眠,当到达最近要执行任务的开始时间点,TimerThread 被唤醒并执行该任务。之后 TimerThread 更新最近一个要执行的任务,继续休眠。

Timer 的优点在于简单易用,但由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务(这点需要注意)。

ScheduledExecutor

鉴于 Timer 的上述缺陷,Java 5 推出了基于线程池设计的 ScheduledExecutor。其设计思想是,每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。需 要注意的是,只有当任务的执行时间到来时,ScheduedExecutor 才会真正启动一个线程,其余时间 ScheduledExecutor 都是在轮询任务的状态。

package com.ibm.scheduler;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledExecutorTest implements Runnable {
 private String jobName = "";

 public ScheduledExecutorTest(String jobName) {
  super();
  this.jobName = jobName;
 }

 @Override
 public void run() {
  System.out.println("execute " + jobName);
 }

 public static void main(String[] args) {
  ScheduledExecutorService service = Executors.newScheduledThreadPool(10);

  long initialDelay1 = 1;
  long period1 = 1;
  // 从现在开始1秒钟之后,每隔1秒钟执行一次job1
  service.scheduleAtFixedRate(
    new ScheduledExecutorTest("job1"),initialDelay1,period1,TimeUnit.SECONDS);

  long initialDelay2 = 1;
  long delay2 = 1;
  // 从现在开始2秒钟之后,每隔2秒钟执行一次job2
  service.scheduleWithFixedDelay(
    new ScheduledExecutorTest("job2"),initialDelay2,TimeUnit.SECONDS);
 }
}
/**
输出结果:
execute job1
execute job1
execute job2
execute job1
execute job1
execute job2
*/

上述代码展示了 ScheduledExecutorService 中两种最常用的调度方法 ScheduleAtFixedRate 和 ScheduleWithFixedDelay。ScheduleAtFixedRate 每次执行时间为上一次任务开始起向后推一个时间间隔,即每次执行时间为 :initialDelay,initialDelay+period,initialDelay+2*period,… ScheduleWithFixedDelay每次执行时间为上一次任务结束起向后推一个时间间隔,即每次执行时间为:initialDelay,initialDelay+executeTime+delay,initialDelay+2*executeTime+2*delay。由此可见,ScheduleAtFixedRate 是基于固定时间间隔进行任务调度,ScheduleWithFixedDelay 取决于每次任务执行的时间长短,是基于不固定时间间隔进行任务调度。

用 ScheduledExecutor 和 Calendar 实现复杂任务调度

Timer 和 ScheduledExecutor 都仅能提供基于开始时间与重复间隔的任务调度,不能胜任更加复杂的调度需求。比如,设置每星期二的 16:38:10 执行任务。该功能使用 Timer 和 ScheduledExecutor 都不能直接实现,但我们可以借助 Calendar 间接实现该功能。

package com.ibm.scheduler;

import java.util.Calendar;
import java.util.Date;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledExceutorTest2 extends TimerTask {

 private String jobName = "";

 public ScheduledExceutorTest2(String jobName) {
  super();
  this.jobName = jobName;
 }

 @Override
 public void run() {
  System.out.println("Date = "+new Date()+",execute " + jobName);
 }

 /**
  * 计算从当前时间currentDate开始,满足条件dayOfWeek,hourOfDay,* minuteOfHour,secondOfMinite的最近时间
  * @return
  */
 public Calendar getEarliestDate(Calendar currentDate,int dayOfWeek,int hourOfDay,int minuteOfHour,int secondOfMinite) {
  //计算当前时间的WEEK_OF_YEAR,DAY_OF_WEEK,HOUR_OF_DAY,MINUTE,SECOND等各个字段值
  int currentWeekOfYear = currentDate.get(Calendar.WEEK_OF_YEAR);
  int currentDayOfWeek = currentDate.get(Calendar.DAY_OF_WEEK);
  int currentHour = currentDate.get(Calendar.HOUR_OF_DAY);
  int currentMinute = currentDate.get(Calendar.MINUTE);
  int currentSecond = currentDate.get(Calendar.SECOND);

  //如果输入条件中的dayOfWeek小于当前日期的dayOfWeek,则WEEK_OF_YEAR需要推迟一周
  boolean weekLater = false;
  if (dayOfWeek < currentDayOfWeek) {
   weekLater = true;
  } else if (dayOfWeek == currentDayOfWeek) {
   //当输入条件与当前日期的dayOfWeek相等时,如果输入条件中的
   //hourOfDay小于当前日期的
   //currentHour,则WEEK_OF_YEAR需要推迟一周 
   if (hourOfDay < currentHour) {
    weekLater = true;
   } else if (hourOfDay == currentHour) {
     //当输入条件与当前日期的dayOfWeek,hourOfDay相等时,
     //如果输入条件中的minuteOfHour小于当前日期的
    //currentMinute,则WEEK_OF_YEAR需要推迟一周
    if (minuteOfHour < currentMinute) {
     weekLater = true;
    } else if (minuteOfHour == currentSecond) {
      //当输入条件与当前日期的dayOfWeek,hourOfDay, 
      //minuteOfHour相等时,如果输入条件中的
     //secondOfMinite小于当前日期的currentSecond,
     //则WEEK_OF_YEAR需要推迟一周
     if (secondOfMinite < currentSecond) {
      weekLater = true;
     }
    }
   }
  }
  if (weekLater) {
   //设置当前日期中的WEEK_OF_YEAR为当前周推迟一周
   currentDate.set(Calendar.WEEK_OF_YEAR,currentWeekOfYear + 1);
  }
  // 设置当前日期中的DAY_OF_WEEK,SECOND为输入条件中的值。
  currentDate.set(Calendar.DAY_OF_WEEK,dayOfWeek);
  currentDate.set(Calendar.HOUR_OF_DAY,hourOfDay);
  currentDate.set(Calendar.MINUTE,minuteOfHour);
  currentDate.set(Calendar.SECOND,secondOfMinite);
  return currentDate;

 }

 public static void main(String[] args) throws Exception {

  ScheduledExceutorTest2 test = new ScheduledExceutorTest2("job1");
  //获取当前时间
  Calendar currentDate = Calendar.getInstance();
  long currentDateLong = currentDate.getTime().getTime();
  System.out.println("Current Date = " + currentDate.getTime().toString());
  //计算满足条件的最近一次执行时间
  Calendar earliestDate = test
    .getEarliestDate(currentDate,3,16,38,10);
  long earliestDateLong = earliestDate.getTime().getTime();
  System.out.println("Earliest Date = "
    + earliestDate.getTime().toString());
  //计算从当前时间到最近一次执行时间的时间间隔
  long delay = earliestDateLong - currentDateLong;
  //计算执行周期为一星期
  long period = 7 * 24 * 60 * 60 * 1000;
  ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
  //从现在开始delay毫秒之后,每隔一星期执行一次job1
  service.scheduleAtFixedRate(test,delay,period,TimeUnit.MILLISECONDS);
 }
}
/**
输出结果:
Current Date = Wed Feb 02 17:32:01 CST 2011
Earliest Date = Tue Feb 8 16:38:10 CST 2011
Date = Tue Feb 8 16:38:10 CST 2011,execute job1
Date = Tue Feb 15 16:38:10 CST 2011,execute job1
*/

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读