您的位置:首页 > 编程语言 > Java开发

java周期调度几种实现

2015-09-28 21:21 337 查看
写代码的时候有些任务需要做周期处理,例如每天9点解析文件并落地到数据库。这时候就需要选择合适的任务调度策略。以下是java里常用的几个任务调度方法:

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);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: