Java任务定时执行器案例的实现

网友投稿 287 2022-07-26


目录⭐️前面的话⭐️1.定时器概述1.1认识定时器1.2Timer类的使用2.定时器的简单实现

⭐️前面的话⭐️

本篇文章将介绍java多线程案例,定时器,定时器就像闹钟一样,等到了指定的时间,闹钟就会发出响声来提醒您,而定时器会执行指定的任务。

1.定时器概述

1.1认识定时器

java中的定时器,也可以叫做任务定时执行器,顾名思义,就是等到了指定的时间,就去做指定的事情,就像你每周六或周日都要去力扣参加周赛一样。

所以你如果想要使用定时器,你需要交代时间和对应的任务才行,java标准也提供了带有定时器功能的类Timer。

1.2Timer类的使用

在java1.8中,Timer给出了四个构造方法,这些构造方法可以去指定线程的名字和是否将定时器内部的线程指定为守护线程。

好了,又出现了一个新概念,这个守护线程是什么鬼?其实在java中有两种线程,一种是用户线程,另外一种是守护线程。用户线程就是普通的线程,守护线程顾名思义就是守护用户线程的线程,可以说就是用户线程的保姆,守护线程与JVM“共存亡”, 只要存在一个用户线程,程序中所有的守护线程都不会停止工作,直到最后一个用户线程执行完毕,守护线程才会停止工作。守护线程最典型的应用就是 GC (垃圾回收器),它就是一个非常称职的守护者。

构造方法:

序号构造方法说明1public Timer()无参数构造方法,默认定时器关联的线程不是守护线程,线程名字也是默认值2public Timer(boolean isDaemon)指定定时器中关联的线程是否为守护线程,如果是,参数为true3public Timer(String name)指定定时器关联线程名称,线程类型默认为非守护线程4public Timer(String name, boolean isDaemon)指定定时器关联线程名和线程类型

Timer类构造时内部也会创建线程,如果不指定,定时器对象内部的线程(为了简化,就称为关联线程吧)的类型是用户线程,而不是守护线程。

核心方法:

序号方法说明1public void schedule(TimerTask task, long delay)指定任务,延迟多久执行该任务2public void schedule(TimerTask task, Date time)指定任务,指定任务的执行时间3public void schedule(TimerTask task, long delay, long period)连续执行指定任务,延迟时间,连续执行任务的时间间隔,毫秒为单位4public void schedule(TimerTask task, Date firstTime, long period)连续执行指定任务,第一次任务的执行时间,连续执行任务的时间间隔5public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)与方法4作用相同6public void scheduleAtFixedRate(TimerTask task, long delay,aEIkKGV long period)与方法3作用相同7public void cancel()终止定时器所有任务,终止执行的任务不受影响

使用演示:

import java.util.Timer;

import java.util.TimerTask;

import java.util.concurrent.PriorityBlockingQueue;

public class TimeProgram {

public static void main(String[] args) throws InterruptedException {

Timer timer = new Timer();

timer.schedule(new TimerTask() {

@Override

public void run() {

System.out.println("执行延后2s执行的任务!");

}

}, 2000);

timer.schedule(new TimerTask() {

@Override

public void run() {

System.out.println("执行延后5s执行的任务!");

}

}, 5000);

//每秒输出一个mian

for (int i = 0; i < 5; i++) {

System.out.println("main");

Thread.sleep(1000);

}

}

}

运行结果:

TimerTask类就是专门描述定时器任务的一个抽象类,它实现了Runnable接口。

public abstract class TimerTask implements Runnable //jdk源码

下面我们简单实现一下定时器,我们就不用TimerTask了,我们直接使用Runnable,因为TimerTask实现了Runnable接口,所以后面测试我们自己所写的schedule方法时,也可以传入TimerTask类型的引用,既然是简单地实现,那就不实现连续执行的功能了。

2.定时器的简单实现

首先,我们需要建造一个类,来描述定时器的任务,可以使用Runnable加上一个任务执行的时间戳就可以了。具体清单:一个构造方法,用来指定任务和延迟执行时间。两个获取方法,用来给外部对象获取该对象的任务和执行时间。实现比较器,用于定时器任务对象的组织,毕竟,每次需要执行时间最早的任务,需要用到基于小根堆实现的优先队列,不,还需要考虑多线程的情况,还是使用优先级阻塞队列吧。

//我的任务

class MyTask implements Comparable {

//接收具体任务

private Runnable runnable;

//执行时的时间戳

private long time;

//构造方法

public MyTask(Runnable runnable, int delay) {

this.runnable = runnable;

this.time = System.currentTimeMillis() + delay;

}

//执行任务

public void run() {

this.runnable.run();

}

//获取执行时间

public long getTime() {

return this.time;

}

//实现comparable接口,方便创建优先级阻塞队列

@Override

public int compareTo(MyTask o) {

return (int) (this.time - o.time);

}

}

接下来就要实现定时器类了,首先我们需要一个数据结构来组织定时器任务,并且每次都能将时间最早的任务找到并执行,那么这个数据结构非小根堆莫属了,也就是优先级队列,注意对自定义类使用优先级队列时,一定要实现比较器。

//每次执行任务时,需要优先执行时间在前的任务,即每次执行任务要选择时间戳最小的任务,在多线程情况中优先级阻塞队列是最佳选择

private static final PriorityBlockingQueue priorityBlockingQueue = new PriorityBlockingQueue<>();

然后,需要一个方法将任务安排在优先级阻塞队列中,最后在构造定时器对象的时候从优先级阻塞队列中取任务并在指定的时间执行。

按照上图的逻辑,我们自己实现的定时器类需要有一个线程专门去执行任务,执行任务过程中可能会遇到执行时间还没有到的情况,那么线程必须得等待,线程等待的方法有两种,一种是wait另一种是sleep,这个案例我们推荐前者,因为sleep方法不能中途唤醒,这个案例是有可能需要中途唤醒的,那就是有新任务插入时,需要重新去优先级阻塞队列拿任务重复上述操作,这个唤醒操作可以使用notify方法实现,所以需要用到wait/notify组合拳,既然需要使用wait/notify那么就得有锁,所以我们可以使用一个专门的锁对象来加锁。

实现代码:

//我的定时类 用来管理任务

class MyTimer {

//专门对锁对象

private final Object locker = new Object();

//每次执行任务时,需要优先执行时间在前的任务,即每次http://执行任务要选择时间戳最小的任务,在多线程情况中优先级阻塞队列是最佳选择

private static final PriorityBlockingQueue priorityBlockingQueue = new PriorityBlockingQueue<>();

//安排任务

public void schedule(Runnable runnable, int delay) {

//将任务放入小根堆中

MyTask task = new MyTask(runnable, delay);

priorityBlockingQueuehttp://.put(task);

//每次当新任务加载到阻塞队列时,需要中途唤醒线程,因为新进来的任务可能是最早需要执行的

synchronized (locker) {

locker.notify();

}

}

public MyTimer() {

Thread thread = new Thread(() -> {

while (true) {

try {

//加载任务,确定执行时机

MyTask myTask = priorityBlockingQueue.take();

long curTime = System.currentTimeMillis();

//时间未到,将任务放回

aEIkKGV if (curTime < myTask.getTime()) {

synchronized (locker) {

priorityBlockingQueue.put(myTask);

//放回任务后,不能立即就再次取该任务加载,需要设置一个再次加载的等待时间,建议使用wait带参数的方法

//因为wait方法可以使用notify进行中途唤醒,而sleep不能中途唤醒

int delay = (int)(myTask.getTime() - curTime);

locker.wait(delay);

}

} else {

System.out.println(Thread.currentThread().getName() + "线程收到任务,正在执行中!");

myTask.run();

System.out.println(Thread.currentThread().getName() + "线程执行任务完毕,正在等待新任务!");

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

});

//不要忘了启动线程

thread.start();

}

}

上面是我们实现定时器的代码,我们来测试一下:

import java.util.TimerTask;

import java.util.concurrent.PriorityBlockingQueue;

public class TimeProgram {

public static void main(String[] args) throws InterruptedException {

MyTimer timer = new MyTimer();

timer.schedule(new TimerTask() {

@Override

public void run() {

System.out.println("执行延后2s执行的任务!");

}

}, 2000);

timer.schedule(new TimerTask() {

@Override

public void run() {

System.out.println("执行延后5s执行的任务!");

}

}, 5000);

//每秒输出一个mian

for (int i = 0; i < 5; i++) {

System.out.println("main");

Thread.sleep(1000);

}

}

}

执行结果:

好了,任务定时执行器你学会了吗?


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:一文掌握Java中的JWT
下一篇:mybatis中 if
相关文章

 发表评论

暂时没有评论,来抢沙发吧~