ScheduledThreadPoolExecutor与Timer

首先来看一下Timer类

例子如下:

 1 package cn.concurrent.executor;
 2 
 3 import java.util.Timer;
 4 import java.util.TimerTask;
 5 
 6 /**
 7  * Created by spark on 17-9-25.
 8  */
 9 public class TestTimer {
10 
11 
12     static class Reminder {
13         Timer timer;
14 
15         public Reminder(int sec) {
16             timer = new Timer();
17             timer.schedule(new TimerTask() {
18                 @Override
19                 public void run() {
20                     System.out.println("this is Timer.");
21                     timer.cancel();
22                 }
23             }, sec * 1000);
24         }
25     }
26 
27     public static void main(String[] args) {
28         System.out.println("------------------");
29         new Reminder(5);
30         System.out.println("。。。。。。。。。。");
31     }
32 }

运行结果如下:

------------------
..................
this is timer.

Process finished with exit code 0

运行后,前2行很快就会输出,第三行5秒后出现。

从这个例子可以看出一个典型的利用timer执行计划任务的过程如下:

  • new一个TimerTask的子类,重写run方法来指定具体的任务
  • new一个Timer类,Timer.schedule(TimerTask),来运行具体的定时任务。
  • 调用相关调度方法执行计划。这个例子调用的是schedule方法。
  • 任务完成,结束线程。这个例子是调用cancel方法结束线程。

JDK源码来看一下:

 1     public void schedule(TimerTask var1, long var2) {
 2         if(var2 < 0L) {
 3             throw new IllegalArgumentException("Negative delay.");
 4         } else {
 5             this.sched(var1, System.currentTimeMillis() + var2, 0L);
 6         }
 7     }
 8 
 9     public void schedule(TimerTask var1, Date var2) {
10         this.sched(var1, var2.getTime(), 0L);
11     }
12 
13     public void schedule(TimerTask var1, long var2, long var4) {
14         if(var2 < 0L) {
15             throw new IllegalArgumentException("Negative delay.");
16         } else if(var4 <= 0L) {
17             throw new IllegalArgumentException("Non-positive period.");
18         } else {
19             this.sched(var1, System.currentTimeMillis() + var2, -var4);
20         }
21     }
22 
23     public void schedule(TimerTask var1, Date var2, long var3) {
24         if(var3 <= 0L) {
25             throw new IllegalArgumentException("Non-positive period.");
26         } else {
27             this.sched(var1, var2.getTime(), -var3);
28         }
29     }
30 
31     public void scheduleAtFixedRate(TimerTask var1, long var2, long var4) {
32         if(var2 < 0L) {
33             throw new IllegalArgumentException("Negative delay.");
34         } else if(var4 <= 0L) {
35             throw new IllegalArgumentException("Non-positive period.");
36         } else {
37             this.sched(var1, System.currentTimeMillis() + var2, var4);
38         }
39     }
40 
41     public void scheduleAtFixedRate(TimerTask var1, Date var2, long var3) {
42         if(var3 <= 0L) {
43             throw new IllegalArgumentException("Non-positive period.");
44         } else {
45             this.sched(var1, var2.getTime(), var3);
46         }
47     }
48 
49     private void sched(TimerTask var1, long var2, long var4) {
50         if(var2 < 0L) {
51             throw new IllegalArgumentException("Illegal execution time.");
52         } else {
53             if(Math.abs(var4) > 4611686018427387903L) {
54                 var4 >>= 1;
55             }
56 
57             TaskQueue var6 = this.queue;
58             synchronized(this.queue) {
59                 if(!this.thread.newTasksMayBeScheduled) {
60                     throw new IllegalStateException("Timer already cancelled.");
61                 } else {
62                     Object var7 = var1.lock;
63                     synchronized(var1.lock) {
64                         if(var1.state != 0) {
65                             throw new IllegalStateException("Task already scheduled or cancelled");
66                         }
67 
68                         var1.nextExecutionTime = var2;
69                         var1.period = var4;
70                         var1.state = 1;
71                     }
72 
73                     this.queue.add(var1);
74                     if(this.queue.getMin() == var1) {
75                         this.queue.notify();
76                     }
77 
78                 }
79             }
80         }
81     }

  核心方法是 sched(),最终会把任务放入一个TaskQueue的队列中,然后来执行。

 默认情况下,创建的timer线程会一直执行,主要有下面四种方式来终止timer线程:

  • 调用timer的cancle方法
  • 把timer线程设置成daemon线程,(new Timer(true)创建daemon线程),在jvm里,如果所有用户线程结束,那么守护线程也会被终止,不过这种方法一般不用。
  • 当所有任务执行结束后,删除对应timer对象的引用,线程也会被终止。
  • 调用System.exit方法终止程序

代码如下:

1 public void cancel() {
2         TaskQueue var1 = this.queue;
3         synchronized(this.queue) {
4             this.thread.newTasksMayBeScheduled = false;
5             this.queue.clear();
6             this.queue.notify();
7         }
8     }

没有显式的线程stop方法,而是调用了queue的clear方法和queue的notify方法,clear是个自定义方法,notify是Objec自带的方法,很明显是去唤醒wait方法的。

1 void clear() {
2         for(int var1 = 1; var1 <= this.size; ++var1) {
3             this.queue[var1] = null;
4         }
5 
6         this.size = 0;
7     }

 原理是清空正个队列(底层是一个数列)。

看一下Timer的构造器:

 1 public Timer(String var1) {
 2         this.queue = new TaskQueue();
 3         this.thread = new TimerThread(this.queue);
 4         this.threadReaper = new Object() {
 5             protected void finalize() throws Throwable {
 6                 synchronized(Timer.this.queue) {
 7                     Timer.this.thread.newTasksMayBeScheduled = false;
 8                     Timer.this.queue.notify();
 9                 }
10             }
11         };
12         this.thread.setName(var1);
13         this.thread.start();
14     }

里面维护一个线程,然后看一下这个线程,如下:

 

继承Thread类,重写run()方法,看看run()方法的逻辑:

 1  public void run() {
 2         boolean var9 = false;
 3 
 4         try {
 5             var9 = true;
 6             this.mainLoop();
 7             var9 = false;
 8         } finally {
 9             if(var9) {
10                 TaskQueue var4 = this.queue;
11                 synchronized(this.queue) {
12                     this.newTasksMayBeScheduled = false;
13                     this.queue.clear();
14                 }
15             }
16         }
17 
18         TaskQueue var1 = this.queue;
19         synchronized(this.queue) {
20             this.newTasksMayBeScheduled = false;
21             this.queue.clear();
22         }
23     }

 看到调用了一个方法mainLoop();其他的操作都是初始化TaskQueue,并清空TaskQueue.

看一下mainLoop()方法。如下:

 1 private void mainLoop() {
 2         while(true) {
 3             while(true) {
 4                 try {
 5                     TaskQueue var3 = this.queue;
 6                     TimerTask var1;
 7                     boolean var2;
 8                     synchronized(this.queue) {
 9                         while(this.queue.isEmpty() && this.newTasksMayBeScheduled) {
10                             this.queue.wait();
11                         }
12 
13                         if(this.queue.isEmpty()) {
14                             return;
15                         }
16 
17                         var1 = this.queue.getMin();
18                         Object var8 = var1.lock;
19                         long var4;
20                         long var6;
21                         synchronized(var1.lock) {
22                             if(var1.state == 3) {
23                                 this.queue.removeMin();
24                                 continue;
25                             }
26 
27                             var4 = System.currentTimeMillis();
28                             var6 = var1.nextExecutionTime;
29                             if(var2 = var6 <= var4) {
30                                 if(var1.period == 0L) {
31                                     this.queue.removeMin();
32                                     var1.state = 2;
33                                 } else {
34                                     this.queue.rescheduleMin(var1.period < 0L?var4 - var1.period:var6 + var1.period);
35                                 }
36                             }
37                         }
38 
39                         if(!var2) {
40                             this.queue.wait(var6 - var4);
41                         }
42                     }
43 
44                     if(var2) {
45                         var1.run();
46                     }
47                 } catch (InterruptedException var13) {
48                     ;
49                 }
50             }
51         }
52     }

可以看到wait方法,之前的notify就是通知到这个wait,然后clear方法在notify之前做了清空数组的操作,所以会break,线程执行结束,退出。

schedule VS. scheduleAtFixedRate

这两个方法都是任务调度方法,他们之间区别是,schedule会保证任务的间隔是按照定义的period参数严格执行的,如果某一次调度时间比较长,那么后面的时间会顺延,保证调度间隔都是period,而scheduleAtFixedRate是严格按照调度时间来的,如果某次调度时间太长了,那么会通过缩短间隔的方式保证下一次调度在预定时间执行。举个栗子:你每个3秒调度一次,那么正常就是0,3,6,9s这样的时间,如果第二次调度花了2s的时间,如果是schedule,就会变成0,3+2,8,11这样的时间,保证间隔,而scheduleAtFixedRate就会变成0,3+2,6,9,压缩间隔,保证调度时间。

原文地址:https://www.cnblogs.com/lwy19998273333/p/7594475.html