首先来看一下Timer类

例子如下:

 package cn.concurrent.executor;

 import java.util.Timer;
import java.util.TimerTask; /**
* Created by spark on 17-9-25.
*/
public class TestTimer { static class Reminder {
Timer timer; public Reminder(int sec) {
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("this is Timer.");
timer.cancel();
}
}, sec * 1000);
}
} public static void main(String[] args) {
System.out.println("------------------");
new Reminder(5);
System.out.println("。。。。。。。。。。");
}
}

运行结果如下:

------------------
..................
this is timer. Process finished with exit code 0

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

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

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

JDK源码来看一下:

     public void schedule(TimerTask var1, long var2) {
if(var2 < 0L) {
throw new IllegalArgumentException("Negative delay.");
} else {
this.sched(var1, System.currentTimeMillis() + var2, 0L);
}
} public void schedule(TimerTask var1, Date var2) {
this.sched(var1, var2.getTime(), 0L);
} public void schedule(TimerTask var1, long var2, long var4) {
if(var2 < 0L) {
throw new IllegalArgumentException("Negative delay.");
} else if(var4 <= 0L) {
throw new IllegalArgumentException("Non-positive period.");
} else {
this.sched(var1, System.currentTimeMillis() + var2, -var4);
}
} public void schedule(TimerTask var1, Date var2, long var3) {
if(var3 <= 0L) {
throw new IllegalArgumentException("Non-positive period.");
} else {
this.sched(var1, var2.getTime(), -var3);
}
} public void scheduleAtFixedRate(TimerTask var1, long var2, long var4) {
if(var2 < 0L) {
throw new IllegalArgumentException("Negative delay.");
} else if(var4 <= 0L) {
throw new IllegalArgumentException("Non-positive period.");
} else {
this.sched(var1, System.currentTimeMillis() + var2, var4);
}
} public void scheduleAtFixedRate(TimerTask var1, Date var2, long var3) {
if(var3 <= 0L) {
throw new IllegalArgumentException("Non-positive period.");
} else {
this.sched(var1, var2.getTime(), var3);
}
} private void sched(TimerTask var1, long var2, long var4) {
if(var2 < 0L) {
throw new IllegalArgumentException("Illegal execution time.");
} else {
if(Math.abs(var4) > 4611686018427387903L) {
var4 >>= 1;
} TaskQueue var6 = this.queue;
synchronized(this.queue) {
if(!this.thread.newTasksMayBeScheduled) {
throw new IllegalStateException("Timer already cancelled.");
} else {
Object var7 = var1.lock;
synchronized(var1.lock) {
if(var1.state != 0) {
throw new IllegalStateException("Task already scheduled or cancelled");
} var1.nextExecutionTime = var2;
var1.period = var4;
var1.state = 1;
} this.queue.add(var1);
if(this.queue.getMin() == var1) {
this.queue.notify();
} }
}
}
}

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

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

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

代码如下:

 public void cancel() {
TaskQueue var1 = this.queue;
synchronized(this.queue) {
this.thread.newTasksMayBeScheduled = false;
this.queue.clear();
this.queue.notify();
}
}

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

 void clear() {
for(int var1 = 1; var1 <= this.size; ++var1) {
this.queue[var1] = null;
} this.size = 0;
}

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

看一下Timer的构造器:

 public Timer(String var1) {
this.queue = new TaskQueue();
this.thread = new TimerThread(this.queue);
this.threadReaper = new Object() {
protected void finalize() throws Throwable {
synchronized(Timer.this.queue) {
Timer.this.thread.newTasksMayBeScheduled = false;
Timer.this.queue.notify();
}
}
};
this.thread.setName(var1);
this.thread.start();
}

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

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

  public void run() {
boolean var9 = false; try {
var9 = true;
this.mainLoop();
var9 = false;
} finally {
if(var9) {
TaskQueue var4 = this.queue;
synchronized(this.queue) {
this.newTasksMayBeScheduled = false;
this.queue.clear();
}
}
} TaskQueue var1 = this.queue;
synchronized(this.queue) {
this.newTasksMayBeScheduled = false;
this.queue.clear();
}
}

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

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

 private void mainLoop() {
while(true) {
while(true) {
try {
TaskQueue var3 = this.queue;
TimerTask var1;
boolean var2;
synchronized(this.queue) {
while(this.queue.isEmpty() && this.newTasksMayBeScheduled) {
this.queue.wait();
} if(this.queue.isEmpty()) {
return;
} var1 = this.queue.getMin();
Object var8 = var1.lock;
long var4;
long var6;
synchronized(var1.lock) {
if(var1.state == 3) {
this.queue.removeMin();
continue;
} var4 = System.currentTimeMillis();
var6 = var1.nextExecutionTime;
if(var2 = var6 <= var4) {
if(var1.period == 0L) {
this.queue.removeMin();
var1.state = 2;
} else {
this.queue.rescheduleMin(var1.period < 0L?var4 - var1.period:var6 + var1.period);
}
}
} if(!var2) {
this.queue.wait(var6 - var4);
}
} if(var2) {
var1.run();
}
} catch (InterruptedException var13) {
;
}
}
}
}

可以看到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,压缩间隔,保证调度时间。

ScheduledThreadPoolExecutor与Timer的更多相关文章

  1. 【高并发】ScheduledThreadPoolExecutor与Timer的区别和简单示例

    JDK 1.5开始提供ScheduledThreadPoolExecutor类,ScheduledThreadPoolExecutor类继承ThreadPoolExecutor类重用线程池实现了任务的 ...

  2. Android定时器,推荐ScheduledThreadPoolExecutor

    Android定时器,推荐ScheduledThreadPoolExecutor 官方网址:http://developer.android.com/reference/java/util/Timer ...

  3. [转载] java多线程学习-java.util.concurrent详解(三)ScheduledThreadPoolExecutor

    转载自http://janeky.iteye.com/blog/770441 ------------------------------------------------------------- ...

  4. ScheduledThreadPoolExecutor Usage

    refs: https://blog.csdn.net/wenzhi20102321/article/details/78681379 对比一下Timer和ScheduledThreadPoolExe ...

  5. 深入理解Java线程池:ScheduledThreadPoolExecutor

    介绍 自JDK1.5开始,JDK提供了ScheduledThreadPoolExecutor类来支持周期性任务的调度.在这之前的实现需要依靠Timer和TimerTask或者其它第三方工具来完成.但T ...

  6. ScheduledThreadPoolExecutor使用指南

    ScheduledThreadPoolExecutor是Timer的多线程实现版本,JDK官方推荐使用.ScheduledThreadPoolExecutor用于替代Timer.是接口Schedule ...

  7. JUC源码分析-线程池篇(三)ScheduledThreadPoolExecutor

    JUC源码分析-线程池篇(三)ScheduledThreadPoolExecutor ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor.它主要用来在 ...

  8. 实例分析Scheduled Thread Pool Executor与Timer的区别

    摘要:JDK 1.5开始提供Scheduled Thread PoolExecutor类,Scheduled Thread Pool Executor类继承Thread Pool Executor类重 ...

  9. Java定时任务的常用实现

    Java的定时任务有以下几种常用的实现方式: 1)Timer 2)ScheduledThreadPoolExecutor 3)Spring中集成Cron Quartz 接下来依次介绍这几类具体实现的方 ...

随机推荐

  1. MVC5+EF6入门完整教程6:Partial View

    https://i-beta.cnblogs.com/posts/edit 上篇文章提到过Partial和Action这两个helper, 本篇文章主要就结合这两个helper来讲解分部视图(Part ...

  2. 数据库Dao层编增删改查写,数据库事务,数据库升级

    数据库事务 有两个特点 1.安全性 情景:正常的转账行为,这个时候如果出现停电等异常,已经扣钱但是没有加钱:这个时候就可用数据库事务解决问题 2.高效性: 使用数据库事务添加享受同数量的数据,对比耗时 ...

  3. 洛谷P1582 倒水 二进制的相关应用

    https://www.luogu.org/problem/P1582 #include<bits/stdc++.h> using namespace std; long long N,K ...

  4. 题解【洛谷P1725】琪露诺

    题面 典型的单调队列优化\(\text{DP}\)题. 不难想到设\(dp_i\)表示以\(i\)结尾能得到的最大冰冻指数. 这样设的转移方程也很简单:\(dp_i=\max\left\{ dp_j+ ...

  5. Spring解决bean之间的循环依赖

    转自链接:https://blog.csdn.net/lyc_liyanchao/article/details/83099675通过前几节的分析,已经成功将bean实例化,但是大家一定要将bean的 ...

  6. cadence动态铜皮的参数设置

    注意这幅图和上一幅图那个焊盘距离shape的间距,变大了,这个设置很用的.

  7. 从原理到方案,一步步讲解web移动端实现自适应等比缩放

    前言 在移动端做自适应,我们常用的有媒体查询,rem ,em,宽度百分比这几种方案.但是都各有其缺点. 首先拿媒体查询来说,在某一个宽度区间内只能使用一种样式,为了适应不同屏幕要,css的代码量就会增 ...

  8. Android 开发 assets和raw

    在Android Project中,有两个文件夹的数据是不会被编译,以原型的方式打包到APK中,这两个文件夹就是 assets 和 res/raw/ 相同点: 1.数据不会编译成二进制字节码. 2.可 ...

  9. HTTP状态码的浪漫故事

    小明是一个程序员,经常加班,在一个和尚部门,一个妹子都没有,所以一直单身. 404(Not Found):服务器无法根据客户端的请求找到资源(网页) 过年回家,老爸老妈给门开一条缝,先看看小明身后-- ...

  10. CSS的布局之文档流,与行内/块级元素的延伸

    文档流,即(position:stiatic),是html布局机制的默认状态. 文档流在排列的过程中,块级元素从上到下,行内元素,从左到右. ·块级元素 <div> <h系列> ...