ScheduledExecutorService定時週期執行指定的任務 ,關於scheduleAtFixedRate 和 scheduleWithFixedDelay使用區別

在項目中使用到了多線程定時執行指定任務中發現以下一些問題:

/**
     * Creates and executes a periodic action that becomes enabled first
     * after the given initial delay, and subsequently with the given
     * 創建並執行一個在給定的初始延遲之後才啓用的週期操作,然後就按給定的週期執行,
     * period; that is executions will commence after
     * 必須在指定任務完成之後開始。
     * {@code initialDelay} then {@code initialDelay+period}, then
     * {@code initialDelay + 2 * period}, and so on.
     * If any execution of the task
     * encounters an exception, subsequent executions are suppressed.
     * Otherwise, the task will only terminate via cancellation or
     * termination of the executor.  If any execution of this task
     * takes longer than its period, then subsequent executions
     * may start late, but will not concurrently execute.
     *
     * @param command the task to execute
     * @param initialDelay the time to delay first execution
     * @param period the period between successive executions
     * @param unit the time unit of the initialDelay and period parameters
     * @return a ScheduledFuture representing pending completion of
     *         the task, and whose {@code get()} method will throw an
     *         exception upon cancellation
     * @throws RejectedExecutionException if the task cannot be
     *         scheduled for execution
     * @throws NullPointerException if command is null
     * @throws IllegalArgumentException if period less than or equal to zero
     */
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);

command:執行線程
initialDelay:初始化延時
period:兩次開始執行最小間隔時間
unit:計時單位

對於scheduleAtFixedRate方法,當我們要執行的任務大於我們指定的執行間隔時會怎麼樣呢?
對於中文API中的註釋,我們可能會被忽悠,認爲無論怎麼樣,它都會按照我們指定的間隔進行執行,其實當執行任務的時間大於我們指定的間隔時間時,它並不會在指定間隔時開闢一個新的線程併發執行這個任務。而是等待該線程執行完畢。




    /**
     * Creates and executes a periodic action that becomes enabled first
     * after the given initial delay, and subsequently with the
     * 創建並執行一個在給定的初始延遲之後才啓用的週期操作,然後,在一個執行的終止和下一個執行之間的延遲
     * given delay between the termination of one execution and the
     * commencement of the next.  If any execution of the task
     * 之間。
     * encounters an exception, subsequent executions are suppressed.
     * Otherwise, the task will only terminate via cancellation or
     * termination of the executor.
     *如果任務的任何執行遇到異常,則會抑制隨後的執行。否則,任務將僅通過執行程序的取消或終止而終止。

     * @param command the task to execute
     * @param initialDelay the time to delay first execution
     * @param delay the delay between the termination of one
     * execution and the commencement of the next
     * @param unit the time unit of the initialDelay and delay parameters
     * @return a ScheduledFuture representing pending completion of
     *         the task, and whose {@code get()} method will throw an
     *         exception upon cancellation
     * @throws RejectedExecutionException if the task cannot be
     *         scheduled for execution
     * @throws NullPointerException if command is null
     * @throws IllegalArgumentException if delay less than or equal to zero
     */
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);

command:執行線程
initialDelay:初始化延時
period:前一次執行結束到下一次執行開始的間隔時間(間隔執行延遲時間)
unit:計時單位

這個是以delay爲固定延遲時間,按照一定的等待時間來執行任務,initialDelay意義與上面的相同。
如果執行初始化延時爲0秒,間隔2秒,如果執行任務用了10秒,那下個任務開始就是第12秒後開始執行。
這個是優先保證任務執行的間隔。

以上是源碼介紹,翻譯不好,希望大家指出。

這裏用三個定時任務說明下問題,到時根據自己需求制定初時時間、間隔時間、以及線程任務數。

package com;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
 * 
 * @author ZL
 *  2017年8月29日
 */
public class Schedule {
    private static ScheduledExecutorService excutor = Executors.newSingleThreadScheduledExecutor();

    /**
     * scheduleAtFixedRate
     * 按指定頻率週期執行某個任務 <br>
     * 初始化延遲0ms開始執行,每隔2ms重新執行一次任務。
     */
    public void one() {
        excutor.scheduleAtFixedRate(new EchoServerone(), // 執行線程
                0, // 初始化延遲
                2000, // 兩次開始的執行的最小時間間隔
                TimeUnit.MILLISECONDS // 計時單位
        );
    }

    /**
     * scheduleAtFixedRate
     * 按指定頻率週期執行某個任務 <br>
     * 初始化延遲0ms開始執行,每隔2ms重新執行一次任務。
     */
    public void two() {
        excutor.scheduleAtFixedRate(new EchoServertwo(), // 執行線程
                0, // 初始化延遲
                2000, // 兩次開始的執行的最小時間間隔
                TimeUnit.MILLISECONDS // 計時單位
        );
    }

    /**
     * scheduleWithFixedDelay
     * 按指定頻率週期執行某個任務 <br>
     * 初始化延遲0ms開始執行,每隔2ms重新執行一次任務。
     */
    public void three() {
        excutor.scheduleWithFixedDelay(new EchoServerthree(), // 執行線程
                0, // 初始化延遲
                2000, // 前一次執行結束到下一次執行開始的間隔時間
                TimeUnit.MILLISECONDS);
    }



    public static void main(String[] args) {

        Schedule schedule = new Schedule();
        schedule.one();
         schedule.two();
         schedule.three();
    }

}
package com;


/**
 * 線程任務一
 * @author ZL
 *  2017年8月29日
 */
public class EchoServerone implements Runnable {

    @Override
    public void run() {
        System.out.println("線程任務一開始執行"+DateUtils.getnewTime());

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("線程任務一執行結束"+DateUtils.getnewTime());

    }

}
package com;

/**
 * 線程任務二
 * @author ZL
 *  2017年8月29日
 */
public class EchoServertwo implements Runnable {
    @Override
    public void run() {
        System.out.println("線程任務二開始執行"+DateUtils.getnewTime());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("線程任務二 執行結束"+DateUtils.getnewTime());
    }

}
package com;

/**
 * 線程任務三
 * @author ZL
 *  2017年8月29日
 */
public class EchoServerthree implements Runnable {
    @Override
    public void run() {
        System.out.println("線程任務三開始執行"+DateUtils.getnewTime());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("線程任務三執行結束"+DateUtils.getnewTime());
    }

}

運行結果:

線程任務一開始執行2017-08-29 17:07:25
線程任務一執行結束2017-08-29 17:07:35
線程任務二開始執行2017-08-29 17:07:35
線程任務二 執行結束2017-08-29 17:07:45
線程任務三開始執行2017-08-29 17:07:45
線程任務三執行結束2017-08-29 17:07:55
線程任務一開始執行2017-08-29 17:07:55
線程任務一執行結束2017-08-29 17:08:05
線程任務二開始執行2017-08-29 17:08:05
線程任務二 執行結束2017-08-29 17:08:15
線程任務一開始執行2017-08-29 17:08:15
線程任務一執行結束2017-08-29 17:08:25
線程任務二開始執行2017-08-29 17:08:25
線程任務二 執行結束2017-08-29 17:08:35
線程任務一開始執行2017-08-29 17:08:35
線程任務一執行結束2017-08-29 17:08:45
線程任務二開始執行2017-08-29 17:08:45
線程任務二 執行結束2017-08-29 17:08:55
線程任務一開始執行2017-08-29 17:08:55

看到運行結果肯定會發現,定時任務三隻在初次運行時執行後由於每個線程執行中都會有10秒的休眠,,然後定時任務三就不運行了,三個定時任務的間隔時間都爲兩秒。

解決辦法是:

修改的代碼:

/**
*   把scheduleWithFixedDelay換成scheduleAtFixedRate
     * 按指定頻率週期執行某個任務 <br>
     * 初始化延遲0ms開始執行,每隔2ms重新執行一次任務。
     */
    public void three() {
        excutor.scheduleAtFixedRate(new EchoServerthree(), // 執行線程
                0, // 初始化延遲
                2000, // 前一次執行結束到下一次執行開始的間隔時間
                TimeUnit.MILLISECONDS);
    }

運行結果:

線程任務一開始執行2017-08-29 17:29:01
線程任務一執行結束2017-08-29 17:29:11
線程任務二開始執行2017-08-29 17:29:11
線程任務二 執行結束2017-08-29 17:29:21
線程任務三開始執行2017-08-29 17:29:21
線程任務三執行結束2017-08-29 17:29:31
線程任務一開始執行2017-08-29 17:29:31
線程任務一執行結束2017-08-29 17:29:41
線程任務二開始執行2017-08-29 17:29:41
線程任務二 執行結束2017-08-29 17:29:51
線程任務三開始執行2017-08-29 17:29:51
線程任務三執行結束2017-08-29 17:30:01
線程任務一開始執行2017-08-29 17:30:01
線程任務一執行結束2017-08-29 17:30:11
線程任務二開始執行2017-08-29 17:30:11
線程任務二 執行結束2017-08-29 17:30:21
線程任務三開始執行2017-08-29 17:30:21
線程任務三執行結束2017-08-29 17:30:31
線程任務一開始執行2017-08-29 17:30:31
線程任務一執行結束2017-08-29 17:30:41
線程任務二開始執行2017-08-29 17:30:41
線程任務二 執行結束2017-08-29 17:30:51

這樣運行後的結果就不會出現有線程任務不執行的後果了。需要那種根據自己需求吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章