本篇文章收录于多线程,也欢迎翻阅博主的其他文章,可能也会让你有不一样的收获
JavaSE多线程数据结构
闹钟大家一定都使用过,当闹钟响的时候,就表示我们该起床了,或者是手机上的定时发短信,等到时间达到时,就会按照我们的要去发送短信等;定时器也就是这样一种工作组件,约定一个时间,当时间达到时,就会执行代码
定时器经常用于网络通信中,例如,当客户端向服务器发出一个请求后,就要等待服务器进行响应,但是,如果服务器一直没响应怎么办,是因为请求没发过去?响应没传过来?服务器出问题了等等,这中间你也不知道发生了什么,这时候,客户端难道就一直等着,等到天荒地老?所以,对于客户端而言,就需要一个等待时间,如果等待时间到了,是重新再发送一遍,还是放弃发送,还是其他的操作等等………
在Java的util包中,提供了一个Timer类,这个类就是一个定时器,下面通过代码演示:
public class MyTimer {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
//给定时器安排一个任务,约定在xxx时间后开始执行
@Override
public void run() {
System.out.println("定时器中任务开始执行");
}
}, 3000);//指定定时时间,约定在三秒之后开始执行
System.out.println("程序执行");
}
}
在执行schedule方法的时候,就是把这个任务放到了timer对象中,而且,在timer对象中,也包含了一个线程,这个线程叫作”扫描线程“,等约定时间到了以后,扫描线程就开始执行这个任务;然后通过执行结果也可以看到,定时器中的任务执行完之后,进程并没有结束,而是处于等待其他任务安排进来,所以由此得出,Timer中的线程会阻止进程结束,在Timer中同样也可以安排多个任务
在学习完如何使用一个定时器后,我们应该还知道如何实现一个简单的定时器
实现一个定时器主要考虑一下几个步骤:
class MyTimerTask {
//顶一个一个任务
private Runnable runnable;
//定义一个定时时间
public long time;
public MyTimerTask(Runnable runnable, long delay) {
this.runnable = runnable;
this.time = System.currentTimeMillis()+delay;
}
public Runnable get_runnable() {
return runnable;
}
}
public class MyTimer {
//定义一个队列用于组织任务
private PriorityQueue<MyTimerTask> priorityQueue = new PriorityQueue<>(new Comparator<MyTimerTask>() {
//自定义比较方法
@Override
public int compare(MyTimerTask o1, MyTimerTask o2) {
return (int)(o1.time - o2.time);
}
});
//定义一个锁对象
Object locker = new Object();
//定义一个schedule方法
public void schedule(Runnable runnable, long time) {
/*
因为是其他的线程调用schedule,然后对队列进行操作,而扫扫描线程也是对队列进行操作,
这也就构成了两个线程对同一个数据进行操作,就可能会出现线程安全问题,因此要进行加锁
*/
synchronized (locker) {
priorityQueue.offer(new MyTimerTask(runnable, time));
locker.notify();
}
}
//在构造方法中定义一个扫描线程
public MyTimer() {
Thread thread = new Thread(() -> {
while(true) {
//检查队列中的任务
check_task();
}
});
thread.start();
}
private void check_task() {
try {
synchronized (locker) {
//当队列为空时,阻塞等待
//使用while进行判断,防止唤醒wait时,队列仍然为空
while (priorityQueue.isEmpty()) {
locker.wait();
}
MyTimerTask myTimerTask = priorityQueue.peek();
//判断当前时间与定时时间
long curTime = System.currentTimeMillis();
if(curTime >= myTimerTask.time) {
//当前时间大于定时时间,开始执行任务
myTimerTask.get_runnable().run();
//任务执行完后删除
priorityQueue.poll();
}else {
//为了防止忙等,在这里进行带有时间的wait等待
//当时间到了以后,再进行循环,开始执行任务
locker.wait(curTime - myTimerTask.time);
}
}
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}