三种时间调度的方式

本文转自 http://blog.csdn.net/adjvet/article/details/48790579


1、java.util.Timer, java.util.TimerTask
timer启动一个异步线程执行任务,周期执行任务策略对应方法scheduleAtFixedRate(TimerTask task, long delay, long period),scheduleAtFixedRate(TimerTask task, Date firstTime, long period)可以指定首次任务时间和周期间隔时间。停止任务执行使用cancel()。
Timer执行任务是单线程的,内部用任务队列来维护待执行任务,任务使用最小堆算法排序(任务下次执行时间距今越小越优先被执行),添加任务时使用锁机制防止并发问题。但是如果自定义的任务处理逻辑包含访问竞争资源,则需要自己进行相关竞争资源的防并发处理。

示例代码如下:

        final Timer timer = new Timer();
        final AtomicInteger taskExecuteCnt = new AtomicInteger();

        TimerTask timerTaskA = new TimerTask() {
            @Override
            public void run() {
                // scheduler work content
                if (taskExecuteCnt.get() >= 10) {
                    timer.cancel();
                }

                System.out.printf("Task A execute,current time:%s\n", new Date());
                taskExecuteCnt.incrementAndGet();
            }
        };

        TimerTask timerTaskB = new TimerTask() {
            @Override
            public void run() {
                // scheduler work content
                if (taskExecuteCnt.get() >= 10) {
                    timer.cancel();
                }

                System.out.printf("Task B execute,current time:%s\n", new Date());
                taskExecuteCnt.incrementAndGet();
            }
        };

        timer.scheduleAtFixedRate(timerTaskA, 0, 500);
        timer.scheduleAtFixedRate(timerTaskB, 0, 500);

执行后结果:
Task A execute,current time:Mon Sep 28 14:08:21 CST 2015
Task B execute,current time:Mon Sep 28 14:08:21 CST 2015
Task B execute,current time:Mon Sep 28 14:08:22 CST 2015
Task A execute,current time:Mon Sep 28 14:08:22 CST 2015
Task A execute,current time:Mon Sep 28 14:08:22 CST 2015
Task B execute,current time:Mon Sep 28 14:08:22 CST 2015
Task B execute,current time:Mon Sep 28 14:08:23 CST 2015
Task A execute,current time:Mon Sep 28 14:08:23 CST 2015
Task A execute,current time:Mon Sep 28 14:08:23 CST 2015
Task B execute,current time:Mon Sep 28 14:08:23 CST 2015
Task B execute,current time:Mon Sep 28 14:08:24 CST 2015

如上,执行顺序为A,B,B,A,A,B,B,A,A,B,B。出现这种同一个任务连续执行是因为Timer内部用队列维护待处理任务,队列排序算法为最小二叉堆(任务下次执行时间距今越小越优先被执行),上述A,B任务的起始执行时间都为0,周期间隔都为500ms。那么待执行任务队列堆如下
       A
   B    A1
B1

A执行完后,经过重排堆变为

        B
   B1   A1

B执行完后,经过重排堆变为

        B1
             A1
所以就会出现堆重排序导致优先级相等的任务执行顺序不固定,如果需要多个任务以特定顺序执行,可通过指定任务首次执行延迟时间来实现,
        timer.scheduleAtFixedRate(timerTaskA, 0, 500);
        timer.scheduleAtFixedRate(timerTaskB, 1, 500);
如上指定,则同一个周期内A任务永远先与B任务执行。

另外,如果某个任务执行时间过长,导致下次触发任务执行时队列中多个任务下次执行时间已到,则会发生多个任务连续执行。如果任务执行时间对执行结果有影响,则合理指定任务周期,单任务处理的数据量,或者使用异步处理机制来确保任务执行正确性(任务触发时只记录一条任务流水,流水记录中包括任务触发时的状态,后续通过额外的恢复任务处理任务流水,异步处理逻辑还可以保证在系统故障时不会丢失队列中待处理的任务)。


2、java.util.concurrent .ScheduledExecutorService

使用java.util.concurrent .ScheduledExecutorService执行任务时,ScheduledExecutorService内部使用线程池执行任务,可以指定执行任务的线程池大小。并发任务较多时可以配置合适的线程池大小提高任务执行效率。


示例代码如下:
        final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
        final AtomicInteger taskExecuteCnt = new AtomicInteger();

        Runnable runnableA = new Runnable() {
            @Override
            public void run() {
                // scheduler work content
                if (taskExecuteCnt.get() >= 10) {
                    scheduledExecutorService.shutdownNow();
                }

                System.out.printf("Task A execute,current time:%s\n", new Date());
                taskExecuteCnt.incrementAndGet();
            }
        };

        Runnable runnableB = new Runnable() {
            @Override
            public void run() {
                // scheduler work content
                if (taskExecuteCnt.get() >= 10) {
                    scheduledExecutorService.shutdownNow();
                }

                System.out.printf("Task B execute,current time:%s\n", new Date());
                taskExecuteCnt.incrementAndGet();
            }
        };

        scheduledExecutorService.scheduleWithFixedDelay(runnableA, 0, 500, TimeUnit.MILLISECONDS);
        scheduledExecutorService.scheduleWithFixedDelay(runnableB, 0, 500,TimeUnit.MILLISECONDS);

这里使用的调度方法是scheduleWithFixedDelay,和scheduleWithFixedRate的区别是:

scheduleWithFixedDelay任务的下次执行时间是根据前次执行结束时间和时间间隔计算得到。
scheduleWithFixedRate任务的下次执行时间是根据前次执行开始时间加时间间隔得到。

使用两个方法可能带来的影响是,如果任务执行时间过长,scheduleWithFixedDelay计算得到的待执行任务个数小于scheduleWithFixedRate计算得到的任务个数。


3、Quartz
Quzrtz支持用cron表达式来定义灵活多变的任务触发时间,例如表达式(0/1 * * * * ?)表示每秒触发任务执行。

spring quarz任务调度示例代码如下:

配置需要的beans,配置文件为quartz.xml:

<?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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
    default-autowire="byName">

    <bean id="cronTaskA" class="com.test.CronTask">
        <property name="jobContent" value="Task A execute" />
    </bean>
    <bean id="cronTaskB" class="com.test.CronTask">
        <property name="jobContent" value="Task B execute" />
    </bean>

    <bean id="jobDetailA"
        class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="cronTaskA" />
        <property name="targetMethod" value="run" />
        <property name="concurrent" value="false" />
    </bean>

    <bean id="jobDetailB"
        class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="cronTaskB" />
        <property name="targetMethod" value="run" />
        <property name="concurrent" value="false" />
    </bean>

    <bean id="cronTriggerA" class="org.springframework.scheduling.quartz.CronTriggerBean">
        <!-- jobDetail -->
        <property name="jobDetail" ref="jobDetailA" />

        <!-- execute per second -->
        <property name="cronExpression" value="0/1 * * * * ?" />
    </bean>

    <bean id="cronTriggerB" class="org.springframework.scheduling.quartz.CronTriggerBean">
        <!-- jobDetail -->
        <property name="jobDetail" ref="jobDetailB" />

        <!-- execute per second -->
        <property name="cronExpression" value="0/1 * * * * ?" />
    </bean>

    <bean id="schedulerFactory"
        class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="cronTriggerA" />
                <ref bean="cronTriggerB" />
            </list>
        </property>
    </bean>
</beans>

自定义任务类实现:
public class CronTask {

    /** logger */
    private static final Logger logger = LoggerFactory.getLogger(CronTask.class);

    /** job content */
    private String              jobContent;

    /**
     * task content
     */
    public void run() {
        if (logger.isInfoEnabled()) {
            logger.info(jobContent);
        }
    }

    /**
     * Setter method for property <tt>jobContent</tt>.
     * 
     * @param jobContent value to be assigned to property jobContent
     */
    public void setJobContent(String jobContent) {
        this.jobContent = jobContent;
    }

}


任务调度测试代码:

        try {
            String xmlPath = System.getProperty("user.dir") +File.separator + "quartz.xml";

            System.out.println(FileUtils.readLines(new File(xmlPath)));

            new FileSystemXmlApplicationContext(xmlPath);

            logger.info("Run QuartzJob");

        } catch (Exception e) {
            logger.error("Exception occurred during quartz job execution.", e);
        }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值