java实现定时任务schedule_定时任务实现原理详解

更新时间:2023-07-08 08:36:55 阅读: 评论:0

~每天早上七点三⼗,准时推送⼲货
正因此,JDK 中的 Timer 定时器由此诞⽣了!
三、最⼩堆实现
所谓最⼩堆⽅案,正如我们上⾯所说的,每当有新任务加⼊的时候,会把需要即将要执⾏的任务排到前⾯,同时会有⼀个线程不断的轮询判断,如果当前某个任务已经到达执⾏时间点,就会⽴即执⾏,具体实现代表就是 JDK 中的 Timer 定时器!
3.1、Timer
⾸先我们来⼀个简单的 Timer 定时器例⼦
public static void main(String[] args) {
Timer timer = new Timer();
//每隔1秒调⽤⼀次
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("test1");
}
}, 1000, 1000);
//每隔3秒调⽤⼀次
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("test2");
}
燕麦鸡蛋饼}, 3000, 3000);
}
实现上,好像跟我们上⾯介绍的 while+sleep ⽅案差不多,同样也是起⼀个TimerTask线程任务,只不过共⽤⼀个Timer调度器。
下⾯我们⼀起来打开源码看看⾥⾯到底有些啥!
进⼊Timer.schedule()⽅法
从⽅法上可以看出,这⾥主要做参数验证,其中TimerTask是⼀个线程任务,delay表⽰延迟多久执⾏(单位毫秒),period表⽰多久执⾏⼀次(单位毫秒)
public void schedule(TimerTask task, long delay, long period) {
if (delay        throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, -period);
}
接着看sched()⽅法
这步操作中,可以很清晰的看到,在同步代码块⾥,会将task对象加⼊到queue
private void sched(TimerTask task, long time, long period) {
if (time 0)
throw new IllegalArgumentException("Illegal execution time.");
// Constrain value of period sufficiently to prevent numeric
// overflow while still being effectively infinitely large.
if (Math.abs(period) > (Long.MAX_VALUE >> 1))
period >>= 1;
synchronized(queue) {
if (!wTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");
synchronized(task.lock) {
if (task.state != TimerTask.VIRGIN)
洗头方法throw new IllegalStateException(
"Task already scheduled or cancelled");
task.period = period;
task.state = TimerTask.SCHEDULED;
}
queue.add(task);
if (Min() == task)水晶宝宝
公务员做什么工作
}
}
我们继续来看queue对象
任务会将⼊到TaskQueue队列中,同时在Timer初始化阶段会将TaskQueue作为参数传⼊到TimerThread线程中,并且起到线程
public class Timer {
private final TaskQueue queue = new TaskQueue();
private final TimerThread thread = new TimerThread(queue);
public Timer() {
this("Timer-" + rialNumber());
}
什么是猎头public Timer(String name) {
thread.tName(name);
thread.start();
}
//...
}
⽽TaskQueue其实是⼀个最⼩堆的数据实体类,源码如下
每当有新元素加⼊的时候,会对原来的数组进⾏重排,会将即将要执⾏的任务排在数组的前⾯
class TaskQueue {
private TimerTask[] queue = new TimerTask[128];
private int size = 0;
void add(TimerTask task) {
// Grow backing store if necessary
if (size + 1 == queue.length)
queue = pyOf(queue, 2*queue.length);
queue[++size] = task;
fixUp(size);
}
private void fixUp(int k) {
while (k > 1) {
int j = k >> 1;
if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
break;
TimerTask tmp = queue[j];
queue[j] = queue[k];
queue[k] = tmp;
k = j;
}
}
//....
}
最后我们来看看TimerThread
TimerThread其实就是⼀个任务调度线程,⾸先从TaskQueue⾥⾯获取排在最前⾯的任务,然后判断它是否到达任务执⾏时间点,如果已到达,就会⽴刻执⾏任务
class TimerThread extends Thread {
boolean newTasksMayBeScheduled = true;
private TaskQueue queue;
TimerThread(TaskQueue queue) {
this.queue = queue;
}
public void run() {
try {
mainLoop();
} finally {
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) {
newTasksMayBeScheduled = fal;
queue.clear();  // Eliminate obsolete references
}
}
}第五节拍
/**
* The main timer loop.  (See class comment.)
* The main timer loop.  (See class comment.)
*/
private void mainLoop() {
while (true) {
try {
TimerTask task;
厨房油烟机清洗boolean taskFired;
synchronized(queue) {
// Wait for queue to become non-empty
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty())
break; // Queue is empty and will forever remain; die
// Queue nonempty; look at first evt and do the right thing
long currentTime, executionTime;
task = Min();
synchronized(task.lock) {
if (task.state == TimerTask.CANCELLED) {
continue;  // No action required, poll queue again
}
currentTime = System.currentTimeMillis();
executionTime = ExecutionTime;
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) { // Non-repeating, remove
task.state = TimerTask.EXECUTED;
} el { // Repeating task, reschedule
task.period<0 ? currentTime  - task.period
: executionTime + task.period);
}
}
}
if (!taskFired) // Task hasn't yet fired; wait
queue.wait(executionTime - currentTime);
}
if (taskFired)  // Task fired; run it, holding no locks
task.run();
} catch(InterruptedException e) {
}
}
}
}
总结这个利⽤最⼩堆实现的⽅案,相⽐ while + sleep ⽅案,多了⼀个线程来管理所有的任务,优点就
是减少了线程之间的性能开销,提升了执⾏效率;但是同样也带来的了⼀些缺点,整体的新加任务写⼊效率变成了 O(log(n))。
同时,细⼼的发现,这个⽅案还有以下⼏个缺点:
串⾏阻塞:调度线程只有⼀个,长任务会阻塞短任务的执⾏,例如,A任务跑了⼀分钟,B任务⾄少需要等1分钟才能跑
容错能⼒差:没有异常处理能⼒,⼀旦⼀个任务执⾏故障,后续任务都⽆法执⾏
3.2、ScheduledThreadPoolExecutor
安于一隅
鉴于 Timer 的上述缺陷,从 Java 5 开始,推出了基于线程池设计的 ScheduledThreadPoolExecutor 。

本文发布于:2023-07-08 08:36:55,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1072729.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:任务   线程   源码   判断   到达   实现   参数
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图