一. 定时器
定时器(定时任务)
- 定时器是一种控制任务延时调用,或者周期调用的技术。
- 作用:闹钟、定时邮件发送。
定时器的实现方式
- 方式一:Timer
- 方式二:ScheduledExecutorService
二. Timer定时器
- TimerTask继承了Runnable:所以TimerTask是线程任务类
2.1 Timer定时器的实现
package com.gch.d8_timer;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/**
目标:Timer定时器的使用和了解
*/
public class TimerDemo1 {
public static void main(String[] args) {
// 1.创建Timer定时器
Timer timer = new Timer(); // 定时器本身就是一个单线程。
// 2.调用方法,处理定时任务
timer.schedule(new TimerTask() {
@Override
public void run() { // 由定时器线程来调的
System.out.println(Thread.currentThread().getName() + "执行一次!"
+ new Date().toLocaleString());
} // 第一次执行 / 启动后首先延迟两秒执行,然后每隔三秒执行一次
},2000,3000);
}
}
2.2 Timer定时器的特点和存在的问题
- 问题1:Timer是单线程,处理多个任务按照顺序执行,存在延时与设置定时器的时间有出入
- 因为Timer是单线程,某些任务忙,其他任务受到干扰,因为其他任务要等它处理完才执行,比如这里,A,B都是每隔两秒执行一次,但是执行到A的时候线程休眠了5秒,导致B已经过了2s的执行周期但因为线程在休眠无法执行,只能线程过了5s休眠才执行B
package com.gch.d8_timer;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/**
目标:Timer定时器存在的问题
问题1:Timer是单线程,处理多个任务按照顺序执行,存在延时与设置定时器的时间有出入
因为Timer是单线程,某些任务忙,其他任务受到干扰,因为其他任务要等它处理完才执行
比如这里,A,B都是每隔两秒执行一次,但是执行到A的时候线程休眠了5秒,导致B已经过了2s的执行周期但因为线程在休眠无法执行,只能线程过了5s休眠才执行B
*/
public class TimerDemo2 {
public static void main(String[] args) {
// 1.创建Timer定时器
Timer timer = new Timer(); // 定时器本身就是一个单线程。
// 2.调用方法,处理定时任务
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行A!" + new Date().toLocaleString());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} // 启动后直接执行,然后每隔两秒执行一次
},0,2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行B!" + new Date().toLocaleString());
}
},0,2000);
}
}
问题二:可能因为其中的某个任务的异常使Timer线程死掉,从而影响后续任务执行。
- 比如这个程序要执行A,B,C三个任务,但是执行到B的时候程序出现异常,导致Timer线程直接挂掉,影响C的执行,导致整个任务全部挂掉。
package com.gch.d8_timer;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/**
Timer定时器存在的问题
问题二:可能因为其中的某个任务的异常使Timer线程死掉,从而影响后续任务执行。
比如这个程序要执行A,B,C三个任务,但是执行到B的时候程序出现异常,导致Timer线程直接挂掉,影响C的执行
*/
public class TimerDemo3 {
public static void main(String[] args) {
// 1.创建Timer定时器
Timer timer = new Timer(); // 定时器本身就是一个单线程。
// 2.调用方法,处理定时任务
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行A!" + new Date().toLocaleString());
} // 启动后直接执行,然后每隔两秒执行一次
},0,2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行B!" + new Date().toLocaleString());
System.out.println(10 / 0); // 出现算术异常,Timer线程直接挂掉,影响后续任务执行
}
},0,2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行C!" + new Date().toLocaleString());
}
},0,3000);
}
}
-
结论:开发中一般不会去用Timer做定时器!
三. ScheduledExecutorService定时器
- ScheduledExecutorService它可以用多个线程去处理多个任务,这样即使某一个线程挂掉了也不会影响其他线程的执行。
- 线程池必然是高级的。
package com.gch.d8_timer;
import java.util.Date;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 目标:ScheduledExecutorService定时器的了解与使用
*/
public class ScheduledExecutorServiceDemo4 {
public static void main(String[] args) {
// 1.创建ScheduledExecutorService线程池,做定时器
ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);
// 2.开启定时任务
// 以固定速率来执行
pool.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行输出A " + new Date().toLocaleString());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},0,2000, TimeUnit.MILLISECONDS);
pool.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(10 / 0); // 有异常,B不执行
System.out.println(Thread.currentThread().getName() + "执行输出B " + new Date().toLocaleString());
}
},0,2000, TimeUnit.MILLISECONDS);
pool.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行输出C " + new Date().toLocaleString());
}
},0,2000, TimeUnit.MILLISECONDS);
}
}