zoukankan      html  css  js  c++  java
  • Java定时任务

    主要有以下三种实现方式:

    • JDK自带:JDK 自带的 Timer 以及 JDK1.5+ 新增的 ScheduledExecutorService;

    • Quartz:简单却强大的 JAVA 作业调度框架;

    • Spring3.0以后自带的task :可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多;

    JDK 自带的定时器实现

    Timer

    这个类允许你调度一个java.util.TimerTask任务。主要有以下几个方法:

    • schedule(TimerTask task, long delay) 延迟 delay 毫秒执行

    • schedule(TimerTask task, Date time) 特定时间执行

    • schedule(TimerTask task, long delay, long period) 延迟 delay 执行并每隔 period 执行一次。用固定延迟调度。使用本方法时,在任务执行中的每一个延迟会传播到后续的任务的执行。

    • scheduleAtFixedRate(TimerTask task, long delay, long period) 延迟 delay 执行并固定速率 period 执行一次。用固定比率调度。使用本方法时,所有后续执行根据初始执行的时间进行调度,从而希望减小延迟。

    ScheduledExecutorService

    该定时任务接口,主要有以下几个方法

    • ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);

    • <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);

    • ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnitunit);

    • ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnitunit);

    该接口的默认实现为 ScheduledThreadPoolExecutor 类,这个类继承了 ThreadPoolExecutor 类。线程池的使用使其比Timer更稳定。spring Task内部也是依靠它实现的。

    Timer的缺陷

    1、Timer在执行定时任务时只会创建一个线程,所以如果存在多个任务,且任务时间过长,超过了两个任务的间隔时间,后面的任务执行时间就被推迟。

    package cn.boomoom.service;
    
    import java.util.Timer;
    import java.util.TimerTask;
    
    public class TimerTest {
    
        private static long start;
    
        public static void main(String[] args) {
    
            TimerTask task1 = new TimerTask() {
                @Override
                public void run() {
                    System.out.println("task1 invoked ! " + (System.currentTimeMillis() - start));
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
    
            TimerTask task2 = new TimerTask() {
                @Override
                public void run() {
                    System.out.println("task2 invoked ! " + (System.currentTimeMillis() - start));
                }
            };
    
            Timer timer = new Timer();
            start = System.currentTimeMillis();
            timer.schedule(task1, 1000);
            timer.schedule(task2, 3000);
        }
    }
    TimerTest

    定义了两个任务,预计是第一个任务1s后执行,第二个任务3s后执行,但是看运行结果:

    task1 invoked ! 1000
    task2 invoked ! 4001

    task2实际上是4后才执行,正因为Timer内部是一个线程,而任务1所需的时间超过了两个任务间的间隔导致。

    package cn.boomoom.service;
    
    import java.util.TimerTask;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    public class ScheduledExecutorServiceTest {
        private static long start;
    
        public static void main(String[] args) {
    
            // 使用工厂方法初始化一个ScheduledThreadPool
            ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(2);
    
            TimerTask task1 = new TimerTask() {
                @Override
                public void run() {
                    try {
                        System.out.println("task1 invoked ! " + (System.currentTimeMillis() - start));
                        Thread.sleep(3000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
    
            TimerTask task2 = new TimerTask() {
                @Override
                public void run() {
                    System.out.println("task2 invoked ! " + (System.currentTimeMillis() - start));
                }
            };
            start = System.currentTimeMillis();
            newScheduledThreadPool.schedule(task1, 1000, TimeUnit.MILLISECONDS);
            newScheduledThreadPool.schedule(task2, 3000, TimeUnit.MILLISECONDS);
        }
    }
    ScheduledExecutorServiceTest
    task1 invoked ! 1002
    task2 invoked ! 3004

    符合我们的预期结果。因为ScheduledThreadPool内部是个线程池,所以可以支持多个任务并发执行。

    2、Timer当任务抛出异常时的缺陷。如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行。

    package cn.boomoom.service;
    
    import java.util.Date;
    import java.util.Timer;
    import java.util.TimerTask;
    
    public class TimerTest01 {
    
        public static void main(String[] args) {
    
            final TimerTask task1 = new TimerTask() {
                @Override
                public void run() {
                    throw new RuntimeException();
                }
            };
    
            final TimerTask task2 = new TimerTask() {
                @Override
                public void run() {
                    System.out.println("task2 invoked!");
                }
            };
    
            Timer timer = new Timer();
            timer.schedule(task1, 100);
            timer.scheduleAtFixedRate(task2, new Date(), 1000);
        }
    }
    TimerTest01
    task2 invoked!
    Exception in thread "Timer-0" java.lang.RuntimeException
        at cn.itcast.bos.service.TimerTest01$1.run(TimerTest01.java:14)
        at java.util.TimerThread.mainLoop(Timer.java:555)
        at java.util.TimerThread.run(Timer.java:505)

    由于任务1的一次,任务2也停止运行了。

    package cn.boomoom.service;
    
    import java.util.TimerTask;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    public class ScheduledExecutorServiceTest01 {
    
        public static void main(String[] args) {
    
            final TimerTask task1 = new TimerTask() {
                @Override
                public void run() { throw new RuntimeException();}
            };
    
            final TimerTask task2 = new TimerTask() {
                @Override
                public void run() { System.out.println("task2 invoked!"); }
            };
    
            ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
            pool.schedule(task1, 100, TimeUnit.MILLISECONDS);
            pool.scheduleAtFixedRate(task2, 0, 1000, TimeUnit.MILLISECONDS);
        }
    }
    ScheduledExecutorServiceTest01

    task2 持续输出。ScheduledExecutorService 保证了,task1出现异常时,不影响task2的运行。

    3、Timer执行周期任务时依赖系统时间,如果当前系统时间发生变化会出现一些执行上的变化,ScheduledExecutorService 基于时间的延迟,不会由于系统时间的改变发生执行变化。

    Quartz 任务调度实现

    quartz 的 java demo

    http://www.quartz-scheduler.org/ 入门案例: http://www.quartz-scheduler.org/documentation/quartz-2.1.x/quick-start.html

    1、导入maven坐标

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>cn.boomoom.maven</groupId>
        <artifactId>quartz_helloworld</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>quartz_helloworld</name>
    
        <dependencies>
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz</artifactId>
                <version>2.2.1</version>
            </dependency>
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz-jobs</artifactId>
                <version>2.2.1</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.12</version>
            </dependency>
        </dependencies>
    </project>
    pom

    2、quartz demo

    package cn.boomoom.service;
    
    import org.quartz.*;
    import org.quartz.impl.StdSchedulerFactory;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class QuartzTest {
        public static void main(String[] args) throws SchedulerException {
            SchedulerFactory sf = new StdSchedulerFactory();
            Scheduler scheduler = sf.getScheduler();
    
            Map<JobDetail, Trigger> jobTriggerMap = jobAndTrigger();
            for (Map.Entry<JobDetail,Trigger> entry : jobTriggerMap.entrySet()) {
                scheduler.scheduleJob(entry.getKey(), entry.getValue());
            }
    
            scheduler.start();
        }
    
        private static Map<JobDetail, Trigger> jobAndTrigger() {
            HashMap<JobDetail, Trigger> hashMap = new HashMap<>();
    
            JobDetail job1 = JobBuilder.newJob(HelloJob1.class).withIdentity("job1", "group1").build();
            Trigger trigger1 = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").startNow()
                    .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();
            hashMap.put(job1, trigger1);
    
            JobDetail job2 = JobBuilder.newJob(HelloJob2.class).withIdentity("job2", "group1").build();
            Trigger trigger2 = TriggerBuilder.newTrigger().forJob("job2", "group1").startNow()
                    .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)).build();
            hashMap.put(job2, trigger2);
    
            JobDetail job3 = JobBuilder.newJob(HelloJob3.class).withIdentity("job3", "group1").build();
            Trigger trigger3 = TriggerBuilder.newTrigger().forJob("job3", "group1").startNow()
                    .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")).build();
            hashMap.put(job3,trigger3);
    
            return hashMap;
        }
    }
    quartzTest

    JobDetail、Trigger、Scheduler三个对象为Quartz主要对象。启动Trigger的定时方式由不同的ScheduleBuilder子类提供,如:SimpleScheduleBuilder、CronScheduleBuilder、DailyTimeIntervalScheduleBuilder、CalendarIntervalScheduleBuilder。

    spring 集成 quartz

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>cn.boomoom.maven</groupId>
      <artifactId>quartz_spring</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <packaging>war</packaging>
      <name>quartz_spring</name>
      
      <dependencies>
              <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz</artifactId>
                <version>2.2.1</version>
            </dependency>
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz-jobs</artifactId>
                <version>2.2.1</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.12</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>4.1.7.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context-support</artifactId>
                <version>4.1.7.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>4.1.7.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>4.1.7.RELEASE</version>
            </dependency>
      </dependencies>
      <build>
          <plugins>
              <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>tomcat-maven-plugin</artifactId>
                  <version>1.1</version>
                  <configuration>
                      <port>9888</port>
                  </configuration>
              </plugin>
          </plugins>
      </build>
    </project>
    1、pom.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://java.sun.com/xml/ns/javaee"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        id="WebApp_ID" version="2.5">
        
        <!-- spring配置文件位置 -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </context-param>
        <!-- spring核心监听器 -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
    </web-app>
    2、web.xml
    package cn.boomoom.job;
    
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.scheduling.quartz.QuartzJobBean;
    
    import cn.boomoom.service.HelloService;
    
    public class HelloJob extends QuartzJobBean {
    
        @Autowired
        private HelloService helloService;
    
        @Override
        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
            helloService.sayHello();
        }
    }
    HelloJob
    package cn.boomoom.service;
    
    import org.springframework.stereotype.Service;
    
    @Service
    public class HelloService {
        public void sayHello() {
            System.out.println("hello,quartz service !");
        }
    }
    HelloService
    package cn.boomoom.job;
    
    import org.quartz.spi.TriggerFiredBundle;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
    import org.springframework.scheduling.quartz.AdaptableJobFactory;
    import org.springframework.stereotype.Service;
    
    @Service("jobFactory")
    public class JobFactory extends AdaptableJobFactory {
    
        @Autowired
        private AutowireCapableBeanFactory capableBeanFactory;
    
        @Override
        protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
            // 调用父类的方法
            Object jobInstance = super.createJobInstance(bundle);
            // 进行注入
            capableBeanFactory.autowireBean(jobInstance);
            return jobInstance;
        }
    }
    JobFactory
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd ">
    
        <context:component-scan base-package="cn.boomoom" />
    
        <bean id="helloJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
            <!--  Job实现类的全路径 -->
            <property name="jobClass" value="cn.boomoom.job.HelloJob"/>  
            <property name="jobDataAsMap">  
                 <map>  
                    <entry key="timeout" value="5"/>
                 </map>  
            </property>  
        </bean> 
    
        <!-- ======================== 调度触发器 ======================== -->
        <bean id="cronTriggerBean" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
            <property name="jobDetail" ref="helloJob"></property>
            <property name="cronExpression" value="0 54 * * * ?"></property>
        </bean>
    
        <!-- ======================== 调度工厂 ======================== -->
        <!-- Scheduler自动运行,不用定义id -->
        <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
            <!-- 在Job中spring管理的Bean无法注入,需要在Scheduler中自定义JobFactory -->
            <property name="jobFactory" ref="jobFactory" />
            <property name="triggers">
                <list>
                    <ref bean="cronTriggerBean" />
                </list>
            </property>
        </bean>
    
    </beans>
    4、applicationContext.xml

    spring提供了JobDetail、Trigger、Scheduler三个的factoryBean。

    在 Job 中 spring 管理的 Bean 无法注入解决,需要在 Scheduler 中自定义 JobFactory。jobFactory 在创建job对象的时候,为其指定了 spring自动注入的属性,(类中的@Autowired)。

    JobFactory 指定定义的时候,上面的方式有些时候不行,可以用下面的方式

    import org.quartz.spi.TriggerFiredBundle;
    import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.scheduling.quartz.SpringBeanJobFactory;
    
    @Service("jobFactory")
    public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements
        ApplicationContextAware {
    
        private transient AutowireCapableBeanFactory beanFactory;
    
        @Override
        public void setApplicationContext(final ApplicationContext context) {
            beanFactory = context.getAutowireCapableBeanFactory();
        }
    
        @Override
        protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
            final Object job = super.createJobInstance(bundle);
            beanFactory.autowireBean(job);
            return job;
        }
    }
    AutowiringSpringBeanJobFactory

     JobFactory 参考1参考2.     quartz定时任务实现只执行一次,SimpleTriggerFactoryBean

    Spring 相关的任务调度

    Spring 3.0+ 自带的任务调度实现,主要依靠TaskScheduler接口的几个实现类实现。
    主要用法有以下三种:

    配置文件实现

      spring-schedule.xml

    <task:scheduler id="myScheduler" pool-size="10" />
    <task:scheduled-tasks scheduler="myScheduler">
        <task:scheduled ref="job" method="test" cron="0 * * * * ?"/>
    </task:scheduled-tasks>

    注解实现

      spring-schedule.xml

    <task:scheduler id="myScheduler" pool-size="10" />
    // 启用注解
    <task:annotation-driven scheduler="myScheduler"/> 
    @Component
    public class ScheduleTask {
    
        // 每隔5秒执行一次
        @Scheduled(cron = "0/5 * * * * ?")
        public void printSay() {
            System.out.println("每隔5秒执行一次:" + new Date());
        } 
    }

    代码动态添加

      spring-schedule.xml

    <bean id = "myScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
        <property name="poolSize" value="10"/>
        <property name="threadGroupName" value="myScheduler" />
        <property name="threadNamePrefix" value="-1" />
    </bean>
    <task:annotation-driven scheduler="myScheduler"/> 
    @Component
    public class Test {
    
        @Autowired
        private ThreadPoolTaskScheduler myScheduler;
    
        public void addJob(){
            myScheduler.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " run ");
                }
            } , new CronTrigger("0/5 * *  * * ? ")); //每5秒执行一次
        }
    }

  • 相关阅读:
    Java--泛型
    Java--文档注释
    Java--网络编程
    Java--序列化
    Java--多线程编程
    Java--ConcurrentHashMap原理分析
    HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别
    面向接口编程详解
    面向接口编程
    base64 数据加密
  • 原文地址:https://www.cnblogs.com/boomoom/p/11299430.html
Copyright © 2011-2022 走看看