前言
在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!”
博客主页:KC老衲爱尼姑的博客主页
博主的github,平常所写代码皆在于此
共勉:talk is cheap, show me the code
作者是爪哇岛的新手,水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!
Java 中的定时器就类似于一个"闹钟",达到一个设定的时间之后,就会执行某个指定的好的代码。就像在学校有早八的课,会设置早上7:30的闹钟提醒自己去上课。所以的当我们使用定时器的时候,需要的设定一个时间和设置好一个对应的任务。Java标准库中提供了带有定时功能的类Timer。
在Java8中,Timer提供了四个构造方法,这些构造方法可以去指定现成的名字和指定定时器内部的线程是否为守护线程。Java中一共有两种线程,一种用户线程,即前台线程,典型的就是main线程,另一种是守护线程(后台线程),典型的是GC(垃圾回收器)。
/**
* 无参构造方法,默认定时器关联的线程不是守护线程,线程的名字也是默认值
*/
public Timer() {
this("Timer-" + serialNumber());
}
/**
* 指定定时器内部的线程是否为守护线程,如果是,参数则为true
*/
public Timer(boolean isDaemon) {
this("Timer-" + serialNumber(), isDaemon);
}
/**
* 指定定时器关联线程的名称,线程类型默认是非守护线程
*/
public Timer(String name) {
thread.setName(name);
thread.start();
}
/**
* 指定定时器关联线程的名称和线程类型
*/
public Timer(String name, boolean isDaemon) {
thread.setName(name);
thread.setDaemon(isDaemon);
thread.start();
}
/**
* 指定任务,延迟多久执行该任务
*/
public void schedule(TimerTask task, long delay) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
sched(task, System.currentTimeMillis()+delay, 0);
}
/**
* 指定任务,指定任务的执行时间
*/
public void schedule(TimerTask task, Date time) {
sched(task, time.getTime(), 0);
}
/**
* 连续执行指定任务,延迟时间,连续执行任务的时间间隔,毫秒为单位
*/
public void schedule(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, -period);
}
/**连续执行指定任务,第一次任务的执行时间,连续执行任务的时间间隔
*
*/
public void schedule(TimerTask task, Date firstTime, long period) {
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, firstTime.getTime(), -period);
}
使用演示:
import java.util.Timer;
import java.util.TimerTask;
public class TimerDemo {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("起床了,要上课了");
}
},1000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("起床了,要上课了,要迟到了");
}
},2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("起床了,太阳都出来了");
}
},3000);
}
}
运行结果:
TimerTask类就是专门描述定时器任务的一个抽象类,它实现了Runnable接口。
定时器的作用是个某个任务设置时间,并让该任务到点执行。由此可知,我们需要一个类来描述任务,并且需要一个基于小根堆的优先级的阻塞队列来管理任务,因为阻塞队列中的任务都有各自的时刻,最先执行的一定是时间最小的,使用优先级的对列就就可以高效的把这个时刻最小的任务取出来执行。
public class MyTask implements Comparable<MyTask>{
private Runnable command;
private long time;
public long getTime() {
return time;
}
public MyTask(Runnable command, long time) {
this.command = command;
//time中存的是绝对时间,超过该时间的任务就应该被执行。
this.time = time;
}
@Override
public int compareTo(MyTask o) {
//谁的时间小排在前面
return (int)(time-o.time);
}
public void run() {
command.run();
}
}
实现定时器类,其核心方法就是schedule,其目的就是为了安排任务。定时器还需要中存在一个 worker 线程, 一直不停的扫描队首元素, 看看是否能执行这个任务,如果这个任务还没有到执行时间,那么这个线程必须等待,在此是用wait等待,因为中途提交的任务可能是最早执行的,这个时候就需要提前唤醒线程,这个唤醒操作使用notify,使用了wait和notify就再提供专门的锁对象来加锁。
import java.util.concurrent.PriorityBlockingQueue;
public class MyTime {
/**
* 锁对象
*/
private final Object lock = new Object();
/**
* 每次执行任务,优先执行时间在前的任务,也就是每次都得从优先级对列中取出时间最小的任务
*/
private static final PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
/**
* 安排任务
* @param runnable
* @param after
*/
public void schedule(Runnable runnable,long after) {
MyTask myTask = new MyTask(runnable,after);
queue.offer(myTask);
synchronized (lock) {
//每次当新任务加载到阻塞队列时,需要中途唤醒线程,因为新进来的任务可能是最早需要执行的
lock.notify();
}
}
public MyTime(){
Worker worker = new Worker();
worker.start();
}
private class Worker extends Thread{
@Override
public void run() {
while (true) {
try {
MyTask task = queue.take();
long currentTime = System.currentTimeMillis();
if (task.getTime() > currentTime) {
synchronized (lock) {
//任务被执行的时间还没有到,把任务塞回队列
queue.put(task);
//设置指定等待时间 wait
lock.wait(task.getTime()-currentTime);
}
}else {
//时间到了,可以执行任务
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
}
}
使用演示:
import java.util.TimerTask;
public class MyTimeDemo {
public static void main(String[] args) {
MyTime myTime = new MyTime();
myTime.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("起床了,要上课了");
}
}, 1000);
myTime.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("起床了,要上课了,要迟到了");
}
}, 2000);
myTime.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("起床了,太阳都出来了");
}
}, 3000);
}
}
各位看官如果觉得文章写得不错,点赞评论关注走一波!谢谢啦!。