zoukankan      html  css  js  c++  java
  • 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,压缩间隔,保证调度时间。

  • 相关阅读:
    古代规模最大的战争:长平之战(做事不能太小气,不同的将领有不同的视角,要智胜,活着很重要)
    聚集索引更新后会不会马上重新排序
    GitHub Pages 搭建流程-基于jekyll-bootstrap
    OpenStack调研
    领域模型设计
    Load ContextCLR 探测
    Sql Server Job 简单使用
    Power Designer导出实体类和NHibernate xml文件
    解决跨域
    性能计数器
  • 原文地址:https://www.cnblogs.com/lwy19998273333/p/7594475.html
Copyright © 2011-2022 走看看