任务调度器和异步执行器

时间:2021-10-08 08:21:05
1.quartz高度抽象为任务、触发器、调度器
任务:job提供需要执行的任务,程序员实现该接口,在执行job时每次都要创建一个job实例,所以jobdetail接收job的实现类创建实例。同时对job的描述也在jobdetail的构造函数中标明,如调度时的组名和job名称。
触发器:规定时间触发规则,有2个类SimpleTrigger(触发一次或固定间隔周期)CronTrigger(实现cron表达式实现复杂方案),其中Trigger可以和quartz定义的Calendar结合针对一些时间进行包含或者排除(与java原生态Calendar不一样),在org.quartz.implCalendar包下有多种实现类。
调度器(scheduler):它是quartz的独立运行容器,Trigger和Jobdetail可以注册到该容器中(有各自的组和名称),trigger的组和名称合起来必须唯一,同理jobdetail也是组和名称合起来必须为,因为类型不相同所以在2个不同集合中。将Trigger和jobdetail进行绑定实现功能执行,一个job对应多个trigger但是一个trigger只能对应一个job。SchedulerFactory创建scheduler实例,在scheduler中有一个schedulerContext,保存着上下文和servletContext类似。
线程池(ThreadPool):scheduler通过线程池对任务进行调用。


状态job:在job中有一个标签子接口(statefulJob)标注接口是有无状态,有状态job表示当次的执行将影响后面的job执行,无状态job则不会。再job中有一个JobDataMap保存着数据,无状态的每次会复制一个该对象,而有状态的会共享该对象。因此无状态可以并发执行,有状态的就需要等待。一般使用无状态job。如果quartz启动数据库持久化任务调度,则无状态的在注册时保存一次,有状态的每次执行都需要保存。
trigger也有自己的jobdatamap属性,但是每次启动时不会持久化到数据库中。


quartz事件机制:job执行前后,trigger触发前后,scheduler调度前后都可以注册事件进行监听。


2.与spring集成
JobDetailFactoryBean属性:
jobclass实现job的类
beanName默认是bean的id
jobDataAsMap给jobdatamap中输入值
applicationContextJobDataKey指定一个名词在job实现类中可以引用spring的应用上下文
jobListenerNames:指定监听的类名


MethodInvokingJobDetailFactoryBean(这种方式不能持久化)
可以实现把一个业务bean的某个方式封装成具有job实现类的形式提供给任务调度器(前提是这个方法不能有参数)


创建触发器bean简单
创建调度器和spring容器的生命周期进行了关联,并且代替了quartz自身的配置文件,可以自定义property配置文件,还有一些配置如:
calendars为调度器添加calendar
jobdetails向调度器添加jobdetail
autoStartup是否自动启动,不启动则需要程序中启动
starupDelay 延迟多少秒启动scheduler
package com.kay.quartz;
public class QuartzJob {
    public void work() {
        System.out.println("Quartz的任务调度!!!");
    }
}




<beans>    
        <!-- 要调用的工作类 -->
        <bean id="quartzJob" class="com.kay.quartz.QuartzJob"></bean>
        <!-- 定义调用对象和调用对象的方法 -->
        <bean id="jobtask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
            <!-- 调用的类 -->
            <property name="targetObject">
                <ref bean="quartzJob"/>
            </property>
            <!-- 调用类中的方法 -->
            <property name="targetMethod">
                <value>work</value>
            </property>
        </bean>
        <!-- 定义触发时间 -->
        <bean id="doTime" class="org.springframework.scheduling.quartz.CronTriggerBean">
            <property name="jobDetail">
                <ref bean="jobtask"/>
            </property>
            <!-- cron表达式 -->
            <property name="cronExpression">
                <value>10,15,20,25,30,35,40,45,50,55 * * * * ?</value>
            </property>
        </bean>
        <!-- 总管理类 如果将lazy-init='false'那么容器启动就会执行调度程序  -->
        <bean id="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
            <property name="triggers">
                <list>
                    <ref bean="doTime"/>
                </list>
            </property>
        </bean>
</beans>


测试程序:
    public static void main(String[] args) {
        System.out.println("Test start.");
        ApplicationContext context = new ClassPathXmlApplicationContext("quartz-config.xml");
        //如果配置文件中将startQuertz bean的lazy-init设置为false 则不用实例化
        //context.getBean("startQuertz");
        System.out.print("Test end..");
    }




3.jdk提供的timer和timertask类:timertask相当于任务,timer相当于调度器


timertask是一个虚类,需要继承重写(有3个重载方法)
timer只能在固定周期时间调用任务方法。所有的timertask都在该timer下执行所有有时候会出现延迟,因此需要每次执行的任务短。
如果要销毁timer需要使用cancel方法。
java线程分为用户线程和守护线程,垃圾回收器就是守护线程,主要是服务用户线程,当用户线程全部执行完成守护线程也会自动终止,设置线程我守护线程方法为:Thread.setDaemon(true);




4.Executor主要实现任务提交和任务执行解耦
http://blog.csdn.net/mack415858775/article/details/51508831
java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行


(1) newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。示例代码如下:
package test;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
public class ThreadPoolExecutorTest {  
 public static void main(String[] args) {  
  ExecutorService cachedThreadPool = Executors.newCachedThreadPool();  
  for (int i = 0; i < 10; i++) {  
   final int index = i;  
   try {  
    Thread.sleep(index * 1000);  
   } catch (InterruptedException e) {  
    e.printStackTrace();  
   }  
   cachedThreadPool.execute(new Runnable() {  
    public void run() {  
     System.out.println(index);  
    }  
   });  
  }  
 }  
}
线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。


(2) newFixedThreadPool(项目用过)
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。示例代码如下:
package test;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
public class ThreadPoolExecutorTest {  
 public static void main(String[] args) {  
  ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);  
  for (int i = 0; i < 10; i++) {  
   final int index = i;  
   fixedThreadPool.execute(new Runnable() {  
    public void run() {  
     try {  
      System.out.println(index);  
      Thread.sleep(2000);  
     } catch (InterruptedException e) {  
      e.printStackTrace();  
     }  
    }  
   });  
  }  
 }  
} 因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。
定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()


(3)  newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:
package test;  
import java.util.concurrent.Executors;  
import java.util.concurrent.ScheduledExecutorService;  
import java.util.concurrent.TimeUnit;  
public class ThreadPoolExecutorTest {  
 public static void main(String[] args) {  
  ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);  
  scheduledThreadPool.schedule(new Runnable() {  
   public void run() {  
    System.out.println("delay 3 seconds");  
   }  
  }, 3, TimeUnit.SECONDS);  
 }  

表示延迟3秒执行。
定期执行示例代码如下:
package test;  
import java.util.concurrent.Executors;  
import java.util.concurrent.ScheduledExecutorService;  
import java.util.concurrent.TimeUnit;  
public class ThreadPoolExecutorTest {  
 public static void main(String[] args) {  
  ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);  
  scheduledThreadPool.scheduleAtFixedRate(new Runnable() {  
   public void run() {  
    System.out.println("delay 1 seconds, and excute every 3 seconds");  
   }  
  }, 1, 3, TimeUnit.SECONDS);  
 }  
}  
表示延迟1秒后每3秒执行一次


(4) newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下:
package test;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
public class ThreadPoolExecutorTest {  
 public static void main(String[] args) {  
  ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();  
  for (int i = 0; i < 10; i++) {  
   final int index = i;  
   singleThreadExecutor.execute(new Runnable() {  
    public void run() {  
     try {  
      System.out.println(index);  
      Thread.sleep(2000);  
     } catch (InterruptedException e) {  
      e.printStackTrace();  
     }  
    }  
   });  
  }  
 }  



结果依次输出,相当于顺序执行各个任务。




单独使用timer、quartz、executor创建的线程默认是非守护线程,在程序结束后任然会继续执行,如果使用spring注入将这些调度开启和关闭与spring容器进行关联,spring容器启动调度任务开始,应用程序结束(spring容器关闭)调度任务停止。
创建的静态成员是classloader级别,如果web程序停止这些变量会从JVM中删除,而线程是JVM级别,如果在应用程序中启动线程在应用程序关闭后并不会回收该线程。