深入浅出Java Concurrency : 线程池 part 5 周期性任务调度
周期性任务调度前世
在JDK 5.0之前,java.util.Timer/TimerTask是唯一的内置任务调度方法,而且在很长一段时间里很热衷于使用这
种方式进行周期性任务调度。
首先研究下Timer/TimerTask的特性(至于javax.swing.Timer就不再研究了)。
public void schedule(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, -period);
}
public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, period);
}
public class Timer {
private TaskQueue queue = new TaskQueue();
/**
* The timer thread.
*/
private TimerThread thread = new TimerThread(queue);
java.util.TimerThread.mainLoop()
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// Wait for queue to become non-empty
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty())
break; // Queue is empty and will forever remain; die
。。。。。。
if (!taskFired) // Task hasnt yet fired; wait
queue.wait(executionTime - currentTime);
}
if (taskFired) // Task fired; run it, holding no locks
task.run();
} catch(InterruptedException e) {
}
}
}
上面三段代码反映了Timer/TimerTask的以下特性:
Timer对任务的调度是基于绝对时间的。
所有的TimerTask只有一个线程TimerThread来执行,因此同一时刻只有一个TimerTask在执行。
任何一个TimerTask的执行异常都会导致Timer终止所有任务。
由于基于绝对时间并且是单线程执行,因此在多个任务调度时,长时间执行的任务被执行后有可能导致短
时间任务快速在短时间内被执行多次或者干脆丢弃多个任务。
由于Timer/TimerTask有这些特点(缺陷),因此这就导致了需要一个更加完善的任务调度框架来解决这些
问题。
周期性任务调度今生
java.util.concurrent.ScheduledExecutorService的出现正好弥补了Timer/TimerTask的缺陷。
public inte易做图ce ScheduledExecutorService extends ExecutorService {
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
首先ScheduledExecutorService基于ExecutorService,是一个完整的线程池调度。另外在提供线程池的基础上增加
了四个调度任务的API。
schedule(Runnable command,long delay, TimeUnit unit):在指定的延迟时间一次性启动任务(Runnable),没有
返回值。
schedule(Callable<V> callable, long delay, TimeUnit unit):在指定的延迟时间一次性启动任务(Callable),携带
一个结果。
scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit):建并执行一个在给定初始
延迟后首次启用的定期操作,后续操作具有给定的周期;也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推。如果任务的任何一个执行
遇到异常,则后续执行都会被取消。否则,只能通过执行程序的取消或终止方法来终止该任务。如果此任务的
任何一个执行要花费比其周期更长的时间,则将推迟后续执行,但不会同时执行。
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[
补充:软件开发 , Java ,