目录
[二、Java 标准库中的定时器](#二、Java 标准库中的定时器)
一、定时器的概念
|----------------------------------------------|
| 什么是定时器? |
| 定时器是软件开发中的一个常用且重要组件,作用是在达到设定时间后,执行指定的代码。 |
二、Java 标准库中的定时器
|---------------------------------------------------------------------|
| 1)Timer 类 |
| 在 Java 中,使用 Timer 类实现定时器的功能。Timer 类使用 schedule() 方法为定时器添加待执行任务。 |
|--------------------------------------------------------------|
| 2)schedule() 方法 |
| schedule() 方法需要两个参数,第一个参数是计划执行的任务代码,第二个参数是任务等待多少时间后开始执行。 |
++代码演示 Timer 类的使用:++
java
public class Timer_Demo0 {
public static void main(String[] args) {
//新建定时器;
Timer timer = new Timer();
//设定任务为打印,等待3秒后执行;
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("3号举手");
}
},3000);
//设定任务为打印,等待2秒后执行;
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("2号举手");
}
},2000);
//设定任务为打印,等待1秒后执行;
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("1号举手");
}
},1000);
}
}
//运行结果:
1号举手
2号举手
3号举手
...
程序没有结束运行。
|---------------------------------------------------|
| 3)为什么程序没有结束? |
| Timer 类中,内置的线程是一个前台线程,只要有一个前台线程未结束,程序就不会停止运行。 |
三、实现自己的定时器
|------------------------------------------------------------------------------------------------------|
| 定时器结构分析 |
| 定时器可以设定多个任务,这些任务根据时间的先后,按顺序执行。 把这些任务放在一个队列中,每次要执行的任务,肯定是离计划时间最近的那个。 因此,使用优先级队列就可以达成这一目的。 |
| 优先级队列中需要存放的元素,就是通过 schedule() 方法加入的任务。 每个元素的属性,都应该包括一份可执行的代码和一个设定的时间。 |
| 我们还需要在定时器中有一个工作线程,用于观测优先级队列的队首元素,是否达到了执行时间。 |
++代码演示实现自己的定时器:++
java
//队列中的元素 MyTask 类;
class MyTask implements Comparable<MyTask>{
//任务内容;
private Runnable runnable;
//执行时间,单位是一个毫秒级别的时间戳;
private long time;
public long getTime(){
return time;
}
//使用任务内容和相对时间,构造MyTask;
public MyTask(Runnable runnable,long delay){
this.runnable = runnable;
//系统当前时间+相对时间;
this.time = System.currentTimeMillis()+delay;
}
public void run(){
runnable.run();
}
//作为优先级队列的元素,需要可比较;
@Override
public int compareTo(MyTask o) {
return (int) (this.time - o.time);
}
}
//定时器类;
class MyTimer{
//存放任务的队列;
private PriorityQueue<MyTask> queue = new PriorityQueue<>();
//保持监测的线程;
private Thread t;
//锁对象;
private final Object locker = new Object();
//schedule方法,向优先级队列中添加元素;
public void schedule(Runnable runnable,long delay){
synchronized (locker){
MyTask task = new MyTask(runnable,delay);
queue.offer(task);
//入队列了,有元素了,可以唤醒线程了;
locker.notify();
}
}
//关闭定时器方法;
public void cancel(){
t.interrupt();
}
//定时器构造方法,new定时器时就把监测线程打开了;
public MyTimer(){
t = new Thread(()->{
try {
while (true){
//加锁,下面的判断状态和修改数据的代码需要原子;
synchronized (locker){
//队列空了,线程就等待;
if(queue.isEmpty()){
locker.wait();
}
//查看任务是否到达执行时间;
MyTask task = queue.peek();
long curTime = System.currentTimeMillis();
long taskTime = task.getTime();
if(curTime >= taskTime){
queue.poll();
task.run();
}else {
//还没到达执行时间,则计算还有多久,并根据这个时间等待;
locker.wait(taskTime - curTime);
}
}
}
}catch (InterruptedException e){
//调用 cancel() 方法时,抛出这个异常,结束线程,并执行下述代码;
System.out.println("定时器关闭");
}
});
//线程在计时器被new出来时,就要保持运行的状态,随时监测任务队列;
t.start();
}
}
++代码演示执行自己实现的定时器:++
java
public static void main(String[] args) throws InterruptedException {
//新建一个定时器;
MyTimer timer = new MyTimer();
//添加三个任务;
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("3号举手");
}
},3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("2号举手");
}
},2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("1号举手");
}
},1000);
//等待5秒,此时上述定时器任务已经执行完毕;
Thread.sleep(5000);
//调用 cancel() 方法,关闭定时器。
timer.cancel();
}
阅读指针 -> 《经典设计模式之 -- 线程池》