目录
前言:
在多线程编程中,对于线程的先后顺序和时间的控制十分重要,在固定的时间执行代码对于Java开发非常重要。
序列:多线程 - 010
1.定时器是什么
定时器是软件开发中的一个重要的组件,类似于一个“闹钟”。
一旦定时器达到一个设定的时间之后,就执行某个指定好的代码。
定时器是日常开发中非常常用的组件,比如网络通信中对方5秒内没有返回数据,则尝试重新连接。类似于这个场景,就需要使用到定时器。
2.标椎库中定时器
标椎库中提供了一个Timer类,Timer类的核心的方法为schedule()。
其中schedule()方法包含两个参数,第一个参数指定即将要执行的任务代码,第二个参数指定多长时间之后执行该代码(单位为毫秒)。
标椎库中定时器使用如下:
Timer timer = new Timer();
timer.schedule(new TimerTask() {//此处使用匿名内部类的写法,继承了TimerTask并且创建出一个实例
@Override
public void run() {//重写run方法,通过run方法来描述任务的详细情况
System.out.println("这里是2000毫秒后的执行代码...");
}
},2000);//2000毫秒之后再执行该代码
3.实现定时器
定时器的构成:
- 一个带优先级队列;
- 队列中每一个元素是一个Task对象;
- Task中带有一个时间属性,队首元素就是即将要执行的元素;
- 同时有一个worker线程一直扫描队首元素,看队首元素是否要执行;
定时器具体实现方法如下:
/**
* 定时器-使用一个优先级队列的数据结构存放定时器对象
*/
class MyTimerTask implements Comparable<MyTimerTask>{//实现Comparable接口,对相对时间进行排序
private Runnable runnable;//要有一个执行的任务
private long time;//还要有一个执行任务的时间
//此处的delay就是schedule方法传入的“相对时间”
public MyTimerTask(Runnable runnable,long delay){//创建一个构造方法
this.runnable =runnable;
this.time = System.currentTimeMillis() + delay;//当前时间加相对时间
}
@Override
public int compareTo(MyTimerTask o) {//对于优先级队列来说,要求里边的元素务必是可比较的
//这样的写法,就是让队首元素是最小时间的值
return (int) (this.time - o.time);
//如果是想让队首元素最大时间的值
//return o.time - this.time;
}
public long getTime(){//获取执行任务的时间
return time;
}
public Runnable getRunnable(){//获取线程对象
return runnable;
}
}
class MyTimer{//定时器数据结构类,用来存储定时器
//使用一个数据结构,保存所有要安排的任务
private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();//创建队列,队列中存放MyTimerTask类对象元素
private Object object = new Object();//使用这个对象作为锁对象
public void schedule(Runnable runnable,long delay){//将定时器对象放入优先级队列中
synchronized (object){//操作线程,需要加锁
queue.offer(new MyTimerTask(runnable, delay));
object.notify();//向队列里添加元素之后,就要唤醒扫描队列时为空的wait等待
}
}
//搞一个扫描线程
public MyTimer(){//构造方法,
Thread thread = new Thread(()->{
//扫描线程,就是需要不断地扫描队首元素,看是否到达时间
while (true){
synchronized (object){
//尽量不要使用if作为wait的判定条件
//使用while的目的是为了在wait被唤醒的时候,再次确认以下条件
while (queue.isEmpty()) {//队列为空时,等待队列中进入元素
try {
//这里的wait需要其他的线程唤醒
//添加了新的任务就应该唤醒
object.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
MyTimerTask task = queue.peek();//队列非空,进行扫描队首元素
//比较一下,当前的队首元素是否可以执行了
long curTime = System.currentTimeMillis();//当前时间
if (curTime >= task.getTime()){
//当前时间已经到达了任务时间,就可以开始执行任务了
task.getRunnable().run();//执行任务
queue.poll();//执行后将任务给删除掉
}else {
//当前时间还没有到任务时间,暂时不执行
//暂时啥也不用干
try {
object.wait(task.getTime() - curTime);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
});
thread.start();
}
}
public class Demo02 {//测试类
public static void main(String[] args) {
MyTimer timer = new MyTimer();//创建MyTimer类实例,此时开始timer对象就会开始不断扫描队列,运行时间到达的线程
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);
System.out.println("ZZZ...");
}
}
上述代码的运行结果如下:
“ZZZ...”直接打印,“2000”在两秒之后打印,“3000”在三秒之后打印。
以上就是Java中对于定时器的部分内容。