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

这样运行后的结果就不会出现有线程任务不执行的后果了。需要那种根据自己需求吧。

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