四大经典案例_定时器及java代码实现
大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流
作者简介:
- CSDN java领域新星创作者blog.csdn.net/bug…
- 掘金LV3用户 juejin.cn/user/bug…
- 阿里云社区专家博主,星级博主,developer.aliyun.com/bug…
- 华为云云享专家 bbs.huaweicloud.com/bug…
定时器
定时器是什么
定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定好的代码.
也就是说定时器有像join
和sleep
等待功能,不过他们是基于系统内部的定时器,
而我们要学习的是在java
给我们提供的定时器包装类,用于到了指定时间就执行代码!
并且定时器在我们日常开发中十分常用!
java
给我们提供了专门一个定时器的封装类Timer
在java.util
包下!
Timer
定时器
Timer
类下有一个schedule
方法,用于安排指定的任务和执行时间!
也就达到了定时的效果,如果时间到了,就会执行task
!
schedule
包含两个参数.- 第一个参数指定即将要执行的任务代码,
- 第二个参数指定多长时间之后执行 (单位为毫秒).
//实例
import java.util.Timer;
import java.util.TimerTask;
public class Demo1 {
public static void main(String[] args) {
//在java.util.Timer包下
Timer timer = new Timer();
//timer.schedule()方法传入需要执行的任务和定时时间
//Timer内部有专门的线程负责任务的注册,所以不需要start
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello Timer!");
}
},3000);
//main线程
System.out.println("hello main!");
}
}
我们可以看到我们只需要创建一个Timer
对象,然后调用schedule
返回,传入你要执行的任务,和定时时间便可完成!
定时器实现
我们居然知道java
中定时器的使用,那如何自己实现一个定时器呢!
我们可以通过Timer
中的源码,然后进行操作!
Timer
内部需要什么东西呢!
我们想想Timer
的功能!
可以定时执行任务!(线程)
可以知道任务啥时候执行(时间)
可以将多个任务组织起来对比时间执行
- 描述任务
也就是schedule
方法中传入的TimerTake
创建一个专门表示定时器中的任务
class MyTask{
//任务具体要干啥
private Runnable runnable;
//任务执行时间,时间戳
private long time;
///delay是一个时间间隔
public MyTask(Runnable runnable,long delay){
this.runnable = runnable;
time = System.currentTimeMillis()+delay;
}
public void run(){ //描述任务!
runnable.run();
}
}
-
组织任务
组织任务就是将上述的任务组织起来!
我们知道我们的任务需要在多线程的环境下执行,所以就需要有线程安全,阻塞功能的数据结构!并且我们的任务到了时间就需要执行,也就是需要时刻对任务排序!
所以我们采用PriorityBlockingQueue
优先级队列!阻塞!
但是这里我们使用了优先级队列,我们需要指定比较规则,就是让MyTask
实现Comparable
接口,重写compareTo
方法,指定升序排序,就是小根堆!
-
执行时间到了的任务
我们可以创建一个线程,执行时间到了的任务!
//执行时间到了的任务!
public MyTimer(){
Thread thread = new Thread(()->{
while (true){
try {
MyTask task = queue.take();//获取到队首任务
//比较时间是否到了
//获取当前时间戳
long curTime = System.currentTimeMillis();
if(curTime<task.getTime()){//当前时间戳和该任务需要执行的时间比较
//还未到达执行时间
queue.put(task); //将任务放回
}else{//时间到了,执行任务
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();//启动线程!
}
//定时器完整代码
import java.util.concurrent.PriorityBlockingQueue;
class MyTask implements Comparable<MyTask>{
//任务具体要干啥
private Runnable runnable;
public long getTime() {
return time;
}
//任务执行时间,时间戳
private long time;
///delay是一个时间间隔
public MyTask(Runnable runnable,long delay){
this.runnable = runnable;
time = System.currentTimeMillis()+delay;
}
public void run(){ //描述任务!
runnable.run();
}
@Override
public int compareTo(MyTask o) {
return (int)(this.time - o.time);
}
}
public class MyTimer{
//定时器内部需要存放多个任务
private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
public void schedule(Runnable runnable,long delay){
MyTask task = new MyTask(runnable,delay);//接收一个任务!
queue.put(task);//将任务组织起来
}
//执行时间到了的任务!
public MyTimer(){
Thread thread = new Thread(()->{
while (true){
try {
MyTask task = queue.take();//获取到队首任务
//比较时间是否到了
//获取当前时间戳
long curTime = System.currentTimeMillis();
if(curTime<task.getTime()){//当前时间戳和该任务需要执行的时间比较
//还未到达执行时间
queue.put(task); //将任务放回
}else{//时间到了,执行任务
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();//启动线程!
}
}
//测试
public static void main(String[] args) {
MyTimer myTimer = new MyTimer();
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello Timer");
}
}, 3000);
System.out.println("hello main");
}
我们再来检查一下下面代码存在的问题!
//执行时间到了的任务!
public MyTimer(){
Thread thread = new Thread(()->{
while (true){
try {
MyTask task = queue.take();//获取到队首任务
//比较时间是否到了
//获取当前时间戳
long curTime = System.currentTimeMillis();
if(curTime<task.getTime()){//当前时间戳和该任务需要执行的时间比较
//还未到达执行时间
queue.put(task); //将任务放回
}else{//时间到了,执行任务
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();//启动线程!
}
我们上述代码还存在一定缺陷就是执行线程到了的代码,我们的while
循环一直在处于忙等状态!
就好比生活中:
你9点要去做核酸,然后你过一会就看时间,一会就看时间,感觉就有啥大病一样!
所以我们可以定一个闹钟,到了时间就去,没到时间可以干其他的事情!
此处的线程也是如此!我们这里也可以使用wait
阻塞! 然后到了时间就唤醒,就解决了忙等问题!
我们的wait
可以传入指定的时间,到了该时间就唤醒!!!
我们再思考另一个问题!
如果又加入了新的任务呢?
我们此时也需要唤醒一下线程,让线程重新拿到队首元素!
//最终定时器代码!!!!
import java.util.concurrent.PriorityBlockingQueue;
class MyTask implements Comparable<MyTask>{
//任务具体要干啥
private Runnable runnable;
public long getTime() {
return time;
}
//任务执行时间,时间戳
private long time;
///delay是一个时间间隔
public MyTask(Runnable runnable,long delay){
this.runnable = runnable;
time = System.currentTimeMillis()+delay;
}
public void run(){ //描述任务!
runnable.run();
}
@Override
public int compareTo(MyTask o) {
return (int)(this.time - o.time);
}
}
public class MyTimer{
//定时器内部需要存放多个任务
Object locker = new Object();//锁对象
private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
public void schedule(Runnable runnable,long delay){
MyTask task = new MyTask(runnable,delay);//接收一个任务!
queue.put(task);//将任务组织起来
//每次拿到新的任务就需要唤醒线程,重新得到新的队首元素!
synchronized (locker){
locker.notify();
}
}
//执行时间到了的任务!
public MyTimer(){
Thread thread = new Thread(()->{
while (true){
try {
MyTask task = queue.take();//获取到队首任务
//比较时间是否到了
//获取当前时间戳
long curTime = System.currentTimeMillis();
if(curTime<task.getTime()){//当前时间戳和该任务需要执行的时间比较
//还未到达执行时间
queue.put(task); //将任务放回
//阻塞到该时间唤醒!
synchronized (locker){
locker.wait(task.getTime()-curTime);
}
}else{//时间到了,执行任务
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();//启动线程!
}
}
总结:
- 描述一个任务
runnable + time
- 使用优先级队列组织任务
PriorityBlockingQueue
- 实现
schedule
方法来注册任务到队列 - 创建扫描线程,获取队首元素,判断是否执行
- 注意这里的忙等问题
//最后梳理一遍
import java.util.concurrent.PriorityBlockingQueue;
/**
* Created with IntelliJ IDEA.
* Description:定时器
* User: hold on
* Date: 2022-04-09
* Time: 16:07
*/
//1.描述任务
class Task implements Comparable<Task>{
//任务
private Runnable runnable;
//执行时间
private long time;
public Task(Runnable runnable,long delay){
this.runnable = runnable;//传入任务
//获取任务需要执行的时间戳
time = System.currentTimeMillis() + delay;
}
@Override
public int compareTo(Task o) {//指定比较方法!
return (int) (this.time-o.time);
}
public long getTime() {//传出任务时间
return time;
}
public void run(){
runnable.run();
}
}
//组织任务
class MyTimer1{
private Object locker = new Object();//锁对象
//用于组织任务
private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();
public void schedule(Runnable runnable,long delay){
Task task = new Task(runnable,delay);
queue.put(task);//传入队列中
synchronized (locker){
locker.notify();//唤醒线程
}
}
public MyTimer1(){
//扫描线程获取队首元素,判断执行
Thread thread = new Thread(()->{
while (true){
//获取当前时间戳
long curTimer = System.currentTimeMillis();
try {
Task task = queue.take();//队首元素出队
if(curTimer<task.getTime()){
//还未到达执行时间,返回队首元素
queue.put(task);
synchronized (locker){
//阻塞等待
locker.wait(task.getTime()-curTimer);
}
}else {
//执行任务
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();//启动线程
}
}
public class Demo2 {
public static void main(String[] args) {
MyTimer1 myTimer1 = new MyTimer1();
myTimer1.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello Timer1");
}
},1000);
System.out.println("hello main");
}
}
- 点赞
- 收藏
- 关注作者
评论(0)