任务调度
定义:任务调度是基于给定时间点,给定时间间隔或者给定执行次数自动执行任务。java
Timer
Timer 是jdk提供的一个定时器工具,会在主线程以外起一个单独线程执行指定的计划任务。数组
public class TimerTest extends TimerTask { private String jobName; public TimerTest(String jobName) { this.jobName = jobName; } @Override public void run() { System.out.println("执行:" + jobName); } public static void main(String[] args) { Timer timer = new Timer(); long delay1 = 1 * 1000; long interval1 = 1000; //从如今开始1秒后每隔1秒执行一次job1 timer.schedule(new TimerTest("job1"), delay1, interval1); long delay2 = 2 * 1000; long interval2 = 2000; // 从如今开始2秒钟以后,每隔2秒钟执行一次 job2 timer.schedule(new TimerTest("job2"), delay2, interval2); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } //终止执行 timer.cancel(); } }
Timer 的优势在于简单易用,但因为全部任务都是由同一个线程来调度,所以全部任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到以后的任务并发
ScheduledExecutor
ScheduledExecutor 类则在自定义线程池的基础上增长了周期性执行任务的功能ide
ScheduledThreadPoolExecutor 线程池中的每个线程负责执行一个定时任务,相互之间互不干扰,并发执行,线程池会轮询任务状态,执行时间到来才会真正执行线程。函数
public class ScheduledExecutorTest implements Runnable { private String jobName; public ScheduledExecutorTest(String jobName) { this.jobName = jobName; } public void run() { System.out.println("Start: " + getDate()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("execute " + jobName); System.out.println("End: " + getDate()); } //获取当前时间 public String getDate() { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式 return df.format(new Date());// new Date()为获取当前系统时间 } public static void main(String[] args) { ScheduledExecutorService service = Executors.newScheduledThreadPool(10); long initialDelay1 = 1; long period1 = 2; //固定时间间隔 service.scheduleAtFixedRate(new ScheduledExecutorTest("job1"), initialDelay1, period1, TimeUnit.SECONDS); long initialDelay2 = 1; long peroid2 = 3; //本次执行结束和下次开始执行时间保持在三秒 //每次执行时间:executeTime + peroid executeTime可能会变化,可是peroid不变 /*service.scheduleWithFixedDelay(new ScheduledExecutorTest("job2"), initialDelay2, peroid2, TimeUnit.SECONDS); */ } }
使用Executors.newScheduledThreadPool(10)建立一个核心线程数量为10的线程池。工具
方法区别:
1.scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
参数:this
- Runnable 须要执行的task
- initialDelay 延迟至启动任务的时间
- period 本次开始到下次开始的时间间隔
注:spa
- 若本次job执行时间小于period,本次开始执行和下次开始之间时间间隔固定
- 若本次job执行时间大于period,则本次开始时间和下次开始时间间隔为job执行时间
- 每次执行时间 :executeTime < period ? peroid : executeTime
- 不会由于上一个线程的调度失效延迟而受到影响
2.scheduleWithFixedDelay(Runnable command, long initialDelay, long period, TimeUnit unit)
注:线程
- 本次执行结束和下次开始执行时间保持在三秒
- 每次执行时间:executeTime + peroid executeTime可能会变化,可是peroid不变
- 受到上一个线程调度影响,可能会推迟本次任务调度的执行
实现原理:
ScheduledThreadPool建立的线程池底层使用的是DelayQueuecode
Delayed元素的一个无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的Delayed元素。若是延迟都尚未期满,则队列没有头部,而且poll将返回null。
当一个元素的 getDelay(TimeUnit.NANOSECONDS)方法返回一个小于等于0的值时,将发生到期。即便没法使用take或poll移除未到期的元素,也不会将这些元素做为正常元素对待。例如,size方法同时返回到期和未到期元素的计数。此队列不容许使用null元素
ScheduledThreadPoolExecutor是把任务添加到DelayedWorkQueue中,而DelayedWorkQueue则是相似于DelayQueue,内部维护着一个以时间为前后顺序的优先级队列,使用compareTo()方法使用与DelayedWorkQueue队列对其元素ScheduledThreadPoolExecutor task进行排序
小结:
上面建立线程池建议使用ScheduledThreadPoolExecutor,能够更加精确控制。 构造函数其中一个参数为DelayedWorkQueue,内部经过一个RunnableScheduledFuture数组保存Runnable任务,默认初始长度为16,它是一个优先级队列 它根据这个任务的下次执行时间来比较大小,这样可以保证queue[0]位置上的元素是最近须要执行的任务。