上文讲了怎样使用Java自带的定时器【Java】定时器的简单应用
这篇博客就来讲如何来编写一个自己实现的定时器
1、代码框架
由定时器的使用方法得知,我们在使用定时器的时候会添加一个任务timerTask类,而timer类则是我们行使任务的类,因此可以得出我们需要编写的两个对象:
一个timerTask类,一个timer类
首先写下代码框架
java
class SelfTimerTask{
}
class SelfTimer{
}
public class demo {
public static void main(String[] args) {
}
}
2、SelfTimeTask类
这个类型用以存放我们要执行的任务
(1)成员变量
任务类中有两个成员:一个是Runnable类,用来存放要执行任务的内容;一个是参数time,用来存放执行任务的时间
为了防止内存可见性问题或指令重排序问题,给这两个参数都加上volatile关键字
java
private volatile Runnable runnable;
private volatile long time;
(2)构造方法
接着我们需要给SelfTimerTask类写一个构造方法
注意:上面成员变量time指的是任务执行的绝对时间,而我们传进来的参数delay是任务执行的相对时间(即此刻时间到任务执行绝对时间的差)
任务执行的绝对时间 = 此刻时间 + 相对时间参数
java
public SelfTimerTask(Runnable runnable,long delay){
this.runnable = runnable;
this.time = delay + System.currentTimeMillis();
}
(3)get方法
由于两个成员变量访问限制都为private,所以我们需要写两个get方法
java
public Runnable getRunnable() {
return runnable;
}
public long getTime() {
return time;
}
(4)compareTo方法
因为在任务执行时,要通过比较任务的time参数来进行排序,因此我们需要添加compareTo方法使SelfTimerTask类具有可比性
首先让类继承Comparable类
java
class SelfTimerTask implements Comparable<SelfTimerTask>
接着,重写compareTo方法
java
public int compareTo(SelfTimerTask o) {
return (int) (this.time - o.time);
}
注意:这里到底谁减谁要根据后面的需求定;可以根据调试来确定谁减谁
(5)SelfTimerTask完整代码
java
class SelfTimerTask implements Comparable<SelfTimerTask> {
private volatile Runnable runnable;
private volatile long time;
public SelfTimerTask(Runnable runnable,long delay){
this.runnable = runnable;
this.time = delay + System.currentTimeMillis();
}
@Override
public int compareTo(SelfTimerTask o) {
return (int) (this.time - o.time);
}
public Runnable getRunnable() {
return runnable;
}
public long getTime() {
return time;
}
}
3、SelfTimer类
编写完SelfTimerTask类,我们来编写SelfTimer类
SelfTimer类是用以按照时间先后顺序执行存储在其中的多个SelfTimerTask类中的任务的,因此我们采用优先级队列的数据结构来编写SelfTimerTask类
定义一个优先级队列
java
PriorityQueue<SelfTimerTask> queue = new PriorityQueue<>();
(1)schedule()方法
根据Timer类的使用可知,SelfTimer有一个schedule()方法来添加任务
java
public void schedule(Runnable runnable,long time){
SelfTimerTask task = new SelfTimerTask(runnable,time);
queue.offer(task);
}
由于下面有其他方法也要对queue进行操作,为了线程安全,我们在成员变量里定义一个locker对象
java
Object locker = new Object();
并给添加任务这段代码加上锁
java
public void schedule(Runnable runnable,long time){
SelfTimerTask task = new SelfTimerTask(runnable,time);
synchronized (locker){
queue.offer(task);
locker.notify();
}
}
(2)SelfTimer()方法
因为在用schedule()方法添加任务后,代码自动执行了任务,因此我们需要在构造方法里书写一个线程来执行任务
java
public SelfTimer(){
Thread thread = new Thread(()->{
});
thread.start();
}
下面来完善线程内代码内容
因为需要不停地扫描任务是否到了执行时间,因此我们采用一个while循环
并且由于下面的代码对queue进行了操作,我们需要加锁来保证线程安全
java
public SelfTimer(){
Thread thread = new Thread(()->{
while (true){
synchronized (locker){
}
}
});
thread.start();
}
· 大根堆还是小根堆?
由于我们每次执行的是时间已经到达的任务,那么这个任务的time参数一定是最小的
每次需要获取time最小的任务进行操作,当然是选用小根堆
实现小根堆的方法就是重写类中的compareTo()方法,上文已经阐述过,这里不再赘述
· 队列为空?
如果队列为空,我们则需要进行阻塞,一直到队列非空为止
另一方面,为了防止线程是发生异常而被唤醒,我们采用while循环进行判断
java
while (queue.isEmpty()){
try {
locker.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
· 执行任务
执行任务时,首先判断现在的时间是否已经到达任务执行时间
若已经到了,则执行任务;若没有到,就使任务再阻塞task.getTime()-curTime的时间
之所以选择阻塞,是因为若这时队列中添加进了一个执行时间更靠前的任务,可以唤醒对象重新开始循环
java
SelfTimerTask task = queue.peek();
long curTime = System.currentTimeMillis();
if (task.getTime() <= curTime){
task.getRunnable().run();
queue.poll();
}else {
try {
locker.wait(task.getTime()-curTime);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
(3)SelfTimer完整代码
java
class SelfTimer{
PriorityQueue<SelfTimerTask> queue = new PriorityQueue<>();
Object locker = new Object();
public void schedule(Runnable runnable,long time){
SelfTimerTask task = new SelfTimerTask(runnable,time);
synchronized (locker){
queue.offer(task);
locker.notify();
}
}
public SelfTimer(){
Thread thread = new Thread(()->{
while (true){
synchronized (locker){
while (queue.isEmpty()){
try {
locker.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
SelfTimerTask task = queue.peek();
long curTime = System.currentTimeMillis();
if (task.getTime() <= curTime){
task.getRunnable().run();
queue.poll();
}else {
try {
locker.wait(task.getTime()-curTime);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
});
thread.start();
}
}
4、完整代码
java
import java.util.PriorityQueue;
class SelfTimerTask implements Comparable<SelfTimerTask> {
private volatile Runnable runnable;
private volatile long time;
public SelfTimerTask(Runnable runnable,long delay){
this.runnable = runnable;
this.time = delay + System.currentTimeMillis();
}
@Override
public int compareTo(SelfTimerTask o) {
return (int) (this.time - o.time);
}
public Runnable getRunnable() {
return runnable;
}
public long getTime() {
return time;
}
}
class SelfTimer{
PriorityQueue<SelfTimerTask> queue = new PriorityQueue<>();
Object locker = new Object();
public void schedule(Runnable runnable,long time){
SelfTimerTask task = new SelfTimerTask(runnable,time);
synchronized (locker){
queue.offer(task);
locker.notify();
}
}
public SelfTimer(){
Thread thread = new Thread(()->{
while (true){
synchronized (locker){
while (queue.isEmpty()){
try {
locker.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
SelfTimerTask task = queue.peek();
long curTime = System.currentTimeMillis();
if (task.getTime() <= curTime){
task.getRunnable().run();
queue.poll();
}else {
try {
locker.wait(task.getTime()-curTime);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
});
thread.start();
}
}
public class demo {
public static void main(String[] args) {
SelfTimer timer = new SelfTimer();
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("3000");
}
},3000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("2000");
}
},2000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("1000");
}
},1000);
}
}
运行结果