scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit):创建并执行一个在给定
初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。如果
任务的任一执行遇到异常,就会取消后续执行。否则,只能通过执行程序的取消或终止方法来终止该任务。
上述API解决了以下几个问题:
ScheduledExecutorService任务调度是基于相对时间,不管是一次性任务还是周期性任务都是相对于任务加入
线程池(任务队列)的时间偏移。
基于线程池的ScheduledExecutorService允许多个线程同时执行任务,这在添加多种不同调度类型的任务是非常
有用的。
同样基于线程池的ScheduledExecutorService在其中一个任务发生异常时会退出执行线程,但同时会有新的线程
补充进来进行执行。
ScheduledExecutorService可以做到不丢失任务。
下面的例子演示了一个任务周期性调度的例子。
package xylz.study.concurrency.executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorServiceDemo {
public static void main(String[] args) throws Exception{
ScheduledExecutorService execService = Executors.newScheduledThreadPool(3);
execService.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+" -> "+System.currentTimeMillis());
try {
Thread.sleep(2000L);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 1, 1, TimeUnit.SECONDS);
//
execService.scheduleWithFixedDelay(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+" -> "+System.currentTimeMillis());
try {
Thread.sleep(2000L);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 1, 1, TimeUnit.SECONDS);
Thread.sleep(5000L);
execService.shutdown();
}
}
一次可能的输出如下:
pool-1-thread-1 -> 1294672392657
pool-1-thread-2 -> 1294672392659
pool-1-thread-1 -> 1294672394657
pool-1-thread-2 -> 1294672395659
pool-1-thread-1 -> 1294672396657
在这个例子中启动了默认三个线程的线程池,调度两个周期性任务。第一个任务是每隔1秒执行一次,第二
个任务是以固定1秒的间隔执行,这两个任务每次执行的时间都是2秒。上面的输出有以下几点结论:
任务是在多线程中执行的,同一个任务应该是在同一个线程中执行。
scheduleAtFixedRate是每次相隔相同的时间执行任务,如果任务的执行时间比周期还长,那么下一个任务将
立即执行。例如这里每次执行时间2秒,而周期时间只有1秒,那么每次任务开始执行的间隔时间就是2秒。
scheduleWithFixedDelay描述是下一个任务的开始时间与上一个任务的结束时间间隔相同。流入这里每次执行
时间2秒,而周期时间是1秒,那么两个任务开始执行的间隔时间就是2+1=3秒。
事实上ScheduledExecutorService的实现类ScheduledThreadPoolExecutor是继承线程池类ThreadPoolExecutor的,
因此它拥有线程池的全部特性。但是同时又是一种特殊的线程池,这个线程池的线程数大小不限,任务队列
是基于DelayQueue的无限任务队列。具体的结构和算法在以后的章节中分析。
由于ScheduledExecutorService拥有Timer/TimerTask的全部特性,并且使用更简单,支持并发,而且更安全,
因此没有理由继续使用Timer/TimerTask,完全可以全部替换。需要说明的一点事构造ScheduledExecutorService
线程池的核心线程池大小要根据任务数来定,否则可能导致资源的浪费。