各位小伙伴们,上篇文章我为大家归纳总结了关于线程池的概念,标准库中线程池的构造方法,并且对每个构造对象单独领出来了解了一下,然后使用Java为我们封装好的线程池的使用,最后我们自己模拟实现了一个固定核心线程数的线程池~~~
这边文章,我将继续归纳总结在多线程中的最后一个案例 ---- 定时器
在了解定时器之前,我们也务必捋顺一下之前我们针对多线程编写讨论的其他案例:
1.)单例模式
a.)饿汉模式
b.)懒汉模式
2.)阻塞队列
3.)线程池
什么是定时器
当我们客户端像服务器发送一个请求,客户端需要等待服务端的相应~~
但是客户端也不是一直平白无故的等待,它有一定固定的等待时间,如果超过这个等待的时间,服务器还是没有发送相应,客户端就请求失败!!!这个等待就需要定时器来计算等待时间~~

我们有时候在实际开发中,也希望一些程序在一定时间后才执行,这个时候我们程序员就可以使用定时器来帮助我们。
使用标准库中的 Timer 类
在Java标准库中,大佬们为我们封装实现了定时器类(Timer),我们可以指定一个时间后执行代码任务,然后Timer类内部会有线程帮我们去执行任务,并且内部专门的线程是前台线程。
java
public class Demo {
public static void main(String[] args) {
//使用Timer类
Timer timer = new Timer();
// 3000ms后执行这个任务
timer.schedule(new TimerTask() {
//因为TimerTask内部使用Runnable接口,所以需要重写Runnable中的run方法
public void run() {
System.out.println("hello 3000ms~~~");
}
}, 3000);
// 2000ms后执行这个任务
timer.schedule(new TimerTask() {
public void run() {
System.out.println("hello 2000ms~~~");
}
}, 2000);
// 1000ms后执行这个任务
timer.schedule(new TimerTask() {
public void run() {
System.out.println("hello 1000ms~~~");
}
});
Thread.sleep(4000);
//因为Timer内部的线程是前台线程,所以需要我们手动关闭前台线程
timer.cancel();
}
}
手动模拟实现定时器
使用PirorityQueue ---- 因为我们需要让最早执行的任务在堆顶,就可以直接拿到任务
注意这里面关于synchronized的使用
在代码中,我们首先要分辨 time 和delay:
time:比如现在11:00点,妈妈叫我看着锅,5分钟后关掉煤气,所以time = 11:05
delay:就是妈妈叫我5分钟后关掉煤气,所以5分钟后就是delay
为什么不适用ArrayList???
因为我们要知道哪个任务率先被执行,就需要遍历整个ArrayList,而且我们添加任务的时间是随机的,每次都是从最后一个元素开始添加,所以要判断某个任务执行时间是否到了,就需要遍历ArrayList,就更消耗时间了,还可能出现误差~~~
为什么不适用TreeMap???
因为TreeMap无法拿到第一个元素
java
//实现执行Timer任务的任务类
class MyTimerTask implements Copmarable<MyTimerTask> {
private Runnable runnable;
private long time;//保存什么时刻执行该任务。ms级别的时间戳
public MyTimerTask(Runnable runnable, long delay) {
this.runnable = runnable;
this.time = System.currentTimeMillis() + delay;
}
// 获取当前任务执行的时刻
public long getTime() {
return this.time;
}
// 执行任务
public void run() {
runnable.run();//让Runnable的对象去执行任务
}
//重写CompareTo --- 规定比较方法 ---- 对排序方法
public int CompareTo(MyTimerTask o) {
return (int)()(this.time - o.thime);
}
}
//模拟实现Timer类
class MyTimer {
//使用集合类的堆来存放任务数据
public PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
Object locker = new Object();
public MyTimer() {
//创建线程
Thread t = new Thread(() -> {
try{
//在while死循环中不断从堆获取任务
while(true) {
//记得加synchronized,防止线程被调度产生线程安全问题~~~
synchronized(locker) {
//从堆中取出元素
MyTimerTask task = queue.peek();//查看堆顶任务
//判断堆顶元素是否为空
while(task == null) {
locker.wait();//等待任务添加到堆中
task = queue.peek();//重新获取堆顶元素
}
//获取当前的时间,然后判断是否到达执行的时间
long currentTime = System.currentTimeMillis();
if(currentTime >= task.getTime()) {
task.run();//执行任务
queue.poll();//从堆顶拿走,让下一个任务成为堆顶
}else {
//等待到执行的时间才执行,如果有新的任务进来,也可以唤醒
//避免有更早时间的任务进来被忽略
locker.wait(task.getTime() - currentTime);
}
}
}
}catch(InterruptedException e) {
throw new RuntimeException(e);
}
});
t.start();
}
//提供schedule方法
// 这里的delay是在写方法的时候想要多少秒后才执行此任务!!!
public void schedule(Runnable task, long delay) {
synchronized(locker) {
MyTimeTask task = new MyTiemrTask(runnable, delay);
//将任务加入堆中
queue.add(task);
locker.notify();//往堆中加入任务,并且唤醒wait
}
}
}
public class Demo {
public static void main(String[] args) {
MyTimer timer = new MyTimer();
// 3000ms后执行此任务
timer.schedule(() -> {
System.out.println("hello 3000ms");
}, 3000);
// 2000ms后执行此任务
timer.schedule(() -> {
System.out.println("hello 2000");
}, 2000);
// 1000ms后执行此任务
timer.schedule(() -> {
System.out.println("hello 1000");
}, 1000);
}
}
