资讯专栏INFORMATION COLUMN

Java 定时任务系列(1)- Java原生支持

melody_lql / 2442人阅读

摘要:第一次在指定时间点执行任务,之后每隔时间调用任务一次。定时器已启动每隔小时已经添加任务调度表这个方法在应用服务被移除,没有能力再接受请求的时候被调用。

1、普通thread实现

这是最常见的,创建一个thread,然后让它在while循环里一直运行着,通过sleep方法来达到定时任务的效果。这样可以快速简单的实现,代码如下:

public class Task1 {
public static void main(String[] args) {
  // run in a second
  final long timeInterval = 1000;
  Runnable runnable = new Runnable() {
  public void run() {
    while (true) {
      // ------- code for task to run
      System.out.println("Hello !!");
      // ------- ends here
      try {
       Thread.sleep(timeInterval);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      }
    }
  };
  Thread thread = new Thread(runnable);
  thread.start();
  }
}

2、用Timer和TimerTask

介绍
java.util.Timer定时器,实际上是个线程,定时调度所拥有的TimerTasks。
一个java.util.TimerTask实际上就是一个拥有run方法的类,需要定时执行的代码放到run方法体内,TimerTask一般是以匿名类的方式创建。
上面的实现是非常快速简便的,但它也缺少一些功能。
用Timer和TimerTask的话与上述方法相比有如下好处:
当启动和去取消任务时可以控制
第一次执行任务时可以指定你想要的delay时间
在实现时,Timer类可以调度任务,TimerTask则是通过在run()方法里实现具体任务。
Timer实例可以调度多任务,它是线程安全的。
当Timer的构造器被调用时,它创建了一个线程,这个线程可以用来调度任务。

下面是代码:
Timer 测试类代码:

//import java.util.Date;
import java.util.Timer;

//import com.baibutao.apps.linkshop.uxiang.server.util.DateUtil;


public class TestTimerTask {

        public static void main(String[] args){
            TaskA taskA = new TaskA();
            Timer timer = new Timer();
            timer.schedule(taskA, 5 * 1000, 5 * 1000);

            //Date date = DateUtil.parse("2014-12-04 16:50:00");
            //timer.schedule(taskA, date , 5 * 1000);
            //timer.scheduleAtFixedRate(taskA, date, 5 * 1000);
        }
    }

任务类代码:

import java.util.Date;
import java.util.TimerTask;

import wint.lang.utils.DateUtil;

// 定义一个任务类
public class TaskA extends TimerTask{

    @Override
    public void run() {
        System.out.println(DateUtil.formatFullDate(new Date()));
    }

}

关于Timer类的调用方法:
void java.util.Timer.schedule(TimerTask task, long delay):多长时间(毫秒)后执行任务

void java.util.Timer.schedule(TimerTask task, Date time):设定某个时间执行任务

void java.util.Timer.schedule(TimerTask task, long delay, long period):delay时间后开始执行任务,并每隔period时间调用任务一次。

void java.util.Timer.schedule(TimerTask task, Date firstTime, long period):第一次在指定firstTime时间点执行任务,之后每隔period时间调用任务一次。

void java.util.Timer.scheduleAtFixedRate(TimerTask task, long delay, long period):delay时间后开始执行任务,并每隔period时间调用任务一次。

void java.util.Timer.scheduleAtFixedRate(TimerTask task, long delay, long period):第一次在指定firstTime时间点执行任务,之后每隔period时间调用任务一次。

void java.util.Timer.cancel():终止该Timer

boolean java.util.TimerTask.cancel():终止该TimerTask

可以为每个Timer指定多个TimerTask

虽然可通过void java.util.Timer.schedule(TimerTask task, Date firstTime, long period)方法完成“例如:每天上午10点执行一次”的业务,但该实现是基于进行一天(1000 * 60 * 60 * 24毫秒)进行延迟的机制实现的,并不是指定某个具体时间进行执行的。

对于该种需求,可通过Quartz来进行实现

方法名称schedule()和scheduleAtFixedRate()两者的区别
当需要根据period区间时间循环多次调用任务的时候,会存在两种不同的策略,两种方法提供了不同的策略。
调用方法(1)、(2)只是单次执行,不存在多次调用任务的情况,所以没有提供scheduleAtFixedRate方法的调用方式。
schedule()方法更注重保持间隔时间的稳定:保障每隔period时间可调用一次
scheduleAtFixedRate()方法更注重保持执行频率的稳定:保障多次调用的频率趋近于period时间,如果某一次调用时间大于period,下一次就会尽量小于period,以保障频率接近于period
进一步的说明参见eg366的博客,星星的技术专栏

3、ScheduledExecutorService

ScheduledExecutorService是从Java SE 5的java.util.concurrent里,做为并发工具类被引进的,这是最理想的定时任务实现方式。
相比于上两个方法,它有以下好处:

相比于Timer的单线程,它是通过线程池的方式来执行任务的
可以很灵活的去设定第一次执行任务delay时间
提供了良好的约定,以便设定执行的时间间隔

下面是实现代码,我们通过ScheduledExecutorService#scheduleAtFixedRate展示这个例子,通过代码里参数的控制,首次执行加了delay时间。

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Task3 {
  public static void main(String[] args) {
    Runnable runnable = new Runnable() {
      public void run() {
        // task to run goes here
        System.out.println("Hello !!");
      }
    };
    ScheduledExecutorService service = Executors
                    .newSingleThreadScheduledExecutor();
    service.scheduleAtFixedRate(runnable, 0, 1, TimeUnit.SECONDS);
  }
}

关于ScheduledExecutorService 和 Timer的优劣,Timer没有很好的错误处理机制,请参考苦行僧的博客
关于ScheduledExecutorService 的具体用法,请参照涛涛的博客

4、web容器中利用ServletContextListener实现定时任务

一个实现ServletContextListener接口的类: StatisticsContextListener.java:

package com.ed.cnc.servletListener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import com.ed.cnc.city.StatisticsTask;

/**
 * 统计ContextListener
 * @author westd
 *
 */
/**
 * @author westd
 *
 */
public class StatisticsContextListener implements ServletContextListener
{

    private java.util.Timer timer = null;


    /**
     * 这个方法在Web应用服务做好接受请求的时候被调用。
     * 
     * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
     */
    public void contextInitialized(ServletContextEvent event) 
    {
        timer = new java.util.Timer(true);
        event.getServletContext().log("定时器已启动"); 
        timer.schedule(new StatisticsTask(event.getServletContext()), 0, 60*60*1000);//每隔1小时
        event.getServletContext().log("已经添加任务调度表");
    }


    /**
     * 这个方法在Web应用服务被移除,没有能力再接受请求的时候被调用。
     * 
     * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
     */
    public void contextDestroyed(ServletContextEvent event)
    {
        timer.cancel();
        event.getServletContext().log("定时器销毁");
    }

}

一个继承于TimerTask的一个类:StatisticsTask.java

package com.ed.cnc.city;

import java.util.Calendar;
import java.util.TimerTask;

import javax.servlet.ServletContext;


/**
 * 统计任务
 * @author westd
 *
 */
public class StatisticsTask extends TimerTask
{

    private static final int STATISTICS_SCHEDULE_HOUR = 0;
    private static boolean isRunning = false;
    private ServletContext context = null;

    public StatisticsTask(ServletContext context)
    {
        this.context = context;
    }

    @Override
    public void run()
    {
        Calendar cal = Calendar.getInstance(); 
        //System.out.println(isRunning);
        if (!isRunning) 
        { 
            if (STATISTICS_SCHEDULE_HOUR == cal.get(Calendar.HOUR_OF_DAY)) //查看是否为凌晨
            { 
                isRunning = true; 
                context.log("开始执行指定任务");

                //TODO 添加自定义的详细任务
                executeTask();

                //指定任务执行结束
                isRunning = false;
                context.log("指定任务执行结束"); 
            } 
        } 
        else 
        {
            context.log("上一次任务执行还未结束");
        }

    }
/**
 * 执行任务
 */
public void executeTask()
{
    System.out.println("任务1");
    System.out.println("任务2");
}

}
web.xml中添加如下代码:


        com.ed.cnc.servletListener.StatisticsContextListener

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64207.html

相关文章

  • SpringBoot下使用定时任务的方式全揭秘

    摘要:定时任务间隔时间方式执行一次定时任务线程休眠规定时间类类允许调度一个任务。引入依赖配置测试执行一次定时任务使用注解是为定时任务而生的一个注解,查看注解的源码表达式接收一个。 本文旨在用通俗的语言讲述枯燥的知识 定时任务作为一种系统调度工具,在一些需要有定时作业的系统中应用广泛,如每逢某个时间点统计数据、在将来某个时刻执行某些动作...定时任务在主流开发语言均提供相应的API供开发者调用...

    DevTTL 评论0 收藏0
  • Java 定时任务系列(2)-Spring 定时任务的几种实现

    本文来自网络一些博客的整理(包括gong1208的博客 dary1715的博客) 1、简介 这个系列介绍Spring框架实现定时任务的两种方式以及一些高级的用法,包括: 1、使用Quartz,这是一个功能比较强大的的调度器,可以让你的程序在指定时间执行,也可以按照某一个频度执行,配置起来稍显复杂,稍后会详细介绍。 2、Spring3.0以后自带的task,可以将它看成一个轻量级的Quartz,而且...

    roadtogeek 评论0 收藏0
  • java高并发系列 - 第19天:JUC中的Executor框架详解1,全面掌握java并发相关技术

    摘要:有三种状态运行关闭终止。类类,提供了一系列工厂方法用于创建线程池,返回的线程池都实现了接口。线程池的大小一旦达到最大值就会保持不变,在提交新任务,任务将会进入等待队列中等待。此线程池支持定时以及周期性执行任务的需求。 这是java高并发系列第19篇文章。 本文主要内容 介绍Executor框架相关内容 介绍Executor 介绍ExecutorService 介绍线程池ThreadP...

    icattlecoder 评论0 收藏0
  • jvm原理

    摘要:在之前,它是一个备受争议的关键字,因为在程序中使用它往往收集器理解和原理分析简称,是后提供的面向大内存区数到数多核系统的收集器,能够实现软停顿目标收集并且具有高吞吐量具有更可预测的停顿时间。 35 个 Java 代码性能优化总结 优化代码可以减小代码的体积,提高代码运行的效率。 从 JVM 内存模型谈线程安全 小白哥带你打通任督二脉 Java使用读写锁替代同步锁 应用情景 前一阵有个做...

    lufficc 评论0 收藏0
  • SpringBoot中并发定时任务的实现、动态定时任务的实现(看这一篇就够了)

    摘要:也是自带的一个基于线程池设计的定时任务类。其每个调度任务都会分配到线程池中的一个线程执行,所以其任务是并发执行的,互不影响。 原创不易,如需转载,请注明出处https://www.cnblogs.com/baixianlong/p/10659045.html,否则将追究法律责任!!! 一、在JAVA开发领域,目前可以通过以下几种方式进行定时任务 1、单机部署模式 Timer:jdk中...

    BWrong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<