Java定時任務的經常使用實現

Java的定時任務有如下幾種經常使用的實現方式:html

1)Timerjava

2)ScheduledThreadPoolExecutor面試

3)Spring中集成Cron Quartzspring

接下來依次介紹這幾類具體實現的方式多線程


1. Timer併發

利用Java自帶的定時類java.util.Timer以及java.util.TimerTask共同實現多任務的定時觸發與週期性執行,主要包含如下兩個方法:ide

void schedule(TimerTask task, long delay, long period);
void scheduleAtFixedRate(TimerTask task, long delay, long period);

其中delay表示第一次執行的延遲(毫秒),period表示週期性執行的時間間隔(毫秒)。其中,須要特別注意的是這兩個方法中的period都爲該任務後一次執行的起始時間與前一次執行的起始時間只差,但schedule()方法該任務後一次執行的起始時間並不是固定,而是取決於前一次任務的執行耗時(若是該耗時大於period,那後一次執行必須等待前一次執行完畢後當即執行,因此並非嚴格的時間間隔);反觀scheduleAtFixedRate()方法後一次執行則不受前一次執行耗時的影響,所以若是前一次執行較慢,可能出現兩次執行併發執行的場景。spa

Timer典型的用法以下:.net

long delay = 1000L;
long period = 5000L;
Timer timer = new Timer();
timer.schedule(new TimerTask(){
    @Override
    public void run() {
        // TODO Auto-generated method stub
    }
}, delay, period);

Timer的實現過程須要依賴內部的任務隊列TaskQueue與任務線程TimerThread,其中TaskQueue以最小堆的方式實現任務優先隊列,而TimerThread爲單線程執行線程。所以存在的一個問題就是一旦該單線程在執行某個任務時因爲某些緣由hang住,那後續的其他任務執行都會受到影響。線程


 2. ScheduledThreadPoolExecutor

繼承自ThreadPoolExecutor的java.util.concurrent.ScheduledThreadPoolExecutor也能夠實現多任務的定時觸發與週期性執行,而且一般是多線程執行(線程池的形式),主要包含如下兩個方法:

ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);

其中,scheduleAtFixedRate()方法與Timer的schedule()方法是相似的,後一次執行受到前一次執行耗時的影響;但scheduleWithFixedDelay()方法中的period則表示該任務後一次執行的起始時間與前一次執行的結束時間只差,這點與Timer的scheduleAtFixedRate()方法不一樣

既然ScheduledThreadPoolExecutor繼承自ThreadPoolExecutor,那構成線程池核心的要素仍然是同樣的:

  • int corePoolSize
  • int maximumPoolSize
  • long keepAliveTime
  • BlockingQueue<Runnable> workQueue
  • ThreadFactory threadFactory
  • RejectedExecutionHandler handler

但有所不一樣的是,ScheduledThreadPoolExecutor容許自定義的參數僅包括corePoolSize、threadFactory和handler,而maximumPoolSize恆爲Integer.MAX_VALUE,keepAliveTime恆爲0,workQueue恆爲new DelayedWorkQueue(),這就意味着工做隊列實際上是無界的,且maximumPoolSize是無用的,corePoolSize就是工做線程的總個數(不會再增長)。

public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory,
                                       RejectedExecutionHandler handler) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory, handler);
}

因爲ScheduledThreadPoolExecutor是多個線程組成的線程池,所以不容易出現因爲某個耗時任務致使其他定時任務難以分配線程沒法執行的狀況,所以更建議使用ScheduledThreadPoolExecutor取代Timer。


 3. Cron Quartz

 若是是一個Spring項目,則不妨使用Cron Quartz來更爲靈活地制定定時任務,首先須要在pom.xml中增長對其的依賴配置,以下:

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

隨後,編寫一個XML配置文件(如time-task.xml),用於指定定時任務類和相應的觸發時間等(配置的方法不止一種,但本質上大同小異,這裏僅提供一種做爲參考):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
   <!-- 後臺流量統計定時任務配置 -->
    <bean id="flowDaemon" class="com.xxx.stats.service.impl.GetFlowDaemon">
    </bean>
    <bean id="FlowDaemonDetail"
        class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <!--false表示等上一個任務執行完後再開啓新的任務 -->
        <property name="concurrent" value="false"/>
        <property name="targetObject" ref="flowDaemon"/>
        <property name="targetMethod" value="execute"/>
    </bean>

    <!-- 調度觸發器 -->
    <bean id="FlowDaemonTrigger"
          class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="FlowDaemonDetail"/>
        <property name="cronExpression" value="0 30 0/1 * * ?"/>
    </bean>

    <!-- 調度工廠 -->
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="FlowDaemonTrigger"/>
            </list>
        </property>
    </bean>
</beans>

這裏假設定時任務爲一個後臺流量統計任務,每小時30分觸發一次(配置在cronExpression中),執行的方法爲com.xxx.stats.service.impl.GetFlowDaemon.execute(),且若是前一個任務延後,後一個任務也順延(即不會併發執行)。這種配置的好處就在於全部的配置所有在XML文件中完成,而無需對Java代碼有任何的改動。

相似於0 30 0/1 * * ?的定時任務時間的設定寫法是很是豐富的,支持多樣化的需求,詳見Cron Quartz官方教程

 


 REFERENCES

[1] https://my.oschina.net/pingpangkuangmo/blog/745704

[2] http://www.cnblogs.com/hanganglin/articles/3526240.html

[3] http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/crontrigger.html

[4] https://my.oschina.net/u/2851681/blog/744997

[5] http://www.cnblogs.com/obullxl/archive/2011/07/10/spring-quartz-cron-integration.html

 


爲尊重原創成果,如需轉載煩請註明本文出處:http://www.cnblogs.com/fernandolee24/p/5877516.html,特此感謝

相關文章
相關標籤/搜索