Java定时任务 - Timer 原理
2021/6/22 17:29:44
本文主要是介绍Java定时任务 - Timer 原理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Java定时任务 - Timer 原理
概要
Jdk库自带有两种实现定时任务的技术。一种是通过Timer,另外一个是通过ScheduledThreadPoolExecutor。下面为大家分析Timer实现的原理。
一、Timer
1、Timer使用
public class TimerTest extends TimerTask { @Override public void run() { System.out.println("test1 --------- " + Thread.currentThread()); } static class TimerDemo { public static void main(String[] args) { Timer timer = new Timer(); // 指定三秒后执行TimerTest中的run方法,每间隔1秒执行一次 timer.schedule(new TimerTest(), 3000, 1000); } } }
2、源码分析
看到上面的实践后是不是觉得很简单?接着我们分析下上面的代码。
首先要注意TimerTest这个类继承了TimerTask
TimerTask
public abstract class TimerTask implements Runnable { /** * This object is used to control access to the TimerTask internals. */ final Object lock = new Object(); /** * The state of this task, chosen from the constants below. */ int state = VIRGIN; /** * This task has not yet been scheduled. */ static final int VIRGIN = 0; /** * This task is scheduled for execution. If it is a non-repeating task, * it has not yet been executed. */ static final int SCHEDULED = 1; /** * This non-repeating task has already executed (or is currently * executing) and has not been cancelled. */ static final int EXECUTED = 2; /** * This task has been cancelled (with a call to TimerTask.cancel). */ static final int CANCELLED = 3; /** * Next execution time for this task in the format returned by * System.currentTimeMillis, assuming this task is scheduled for execution. * For repeating tasks, this field is updated prior to each task execution. */ long nextExecutionTime; /** * Period in milliseconds for repeating tasks. A positive value indicates * fixed-rate execution. A negative value indicates fixed-delay execution. * A value of 0 indicates a non-repeating task. */ long period = 0; /** * Creates a new timer task. */ protected TimerTask() { } /** * The action to be performed by this timer task. */ public abstract void run(); public boolean cancel() { synchronized(lock) { boolean result = (state == SCHEDULED); state = CANCELLED; return result; } } public long scheduledExecutionTime() { synchronized(lock) { return (period < 0 ? nextExecutionTime + period : nextExecutionTime - period); } } }
去掉注释后,这个类的代码也就几十行。你可以看到TimerTask实现了Runnable接口,有一个抽象的 run() 方法,我们的TimerTest继承并实现了 run() 方法。以及这个类有几个成员变量:
lock
:用来线程加锁
state
:表示任务的状态
nextExecutionTime
:下一次执行任务的时间
period
:执行任务的周期
而它只有两个方法 :
cancel()
:取消任务,改变任务的state
scheduledExecutionTime()
:设置下次要执行的时间
Timer
我们在来分析下Timer类,先附上一张思维导图,方便记忆理解
下面简单列举Timer关键代码,具体代码可以自己去查看
public class Timer { /** 用来存放要执行的任务,通过最小堆维护的队列 **/ private final TaskQueue queue = new TaskQueue(); /** 负责执行任务的线程 **/ private final TimerThread thread = new TimerThread(queue); /** 构造方法 设置了线程名称 是否为守护进程 最后启动了这个线程 **/ public Timer(String name, boolean isDaemon) { thread.setName(name); thread.setDaemon(isDaemon); thread.start(); } }
在来看看TimerThread这个类
TimerThread
class TimerThread extends Thread { boolean newTasksMayBeScheduled = true; private TaskQueue queue; public void run() { try { mainLoop(); } finally { // Someone killed this Thread, behave as if Timer cancelled synchronized(queue) { newTasksMayBeScheduled = false; queue.clear(); // Eliminate obsolete references } } } /** 关键代码 执行定时任务 **/ private void mainLoop() { while (true) { try { TimerTask task; boolean taskFired; synchronized(queue) { // 如果队列为空,且newTasksMayBeScheduled为true 则线程进入等待状态 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 = queue.getMin(); synchronized(task.lock) { if (task.state == TimerTask.CANCELLED) { // 如果已经是取消状态,则将任务从队列中删除 queue.removeMin(); continue; // No action required, poll queue again } // 获取当前时间 currentTime = System.currentTimeMillis(); // 获取下个任务要执行的时间 executionTime = task.nextExecutionTime; // 如果任务执行时间小于等于当前时间则开始执行 if (taskFired = (executionTime<=currentTime)) { if (task.period == 0) { // Non-repeating, remove queue.removeMin(); task.state = TimerTask.EXECUTED; } else { // Repeating task, reschedule queue.rescheduleMin( task.period<0 ? currentTime - task.period : executionTime + task.period); } } } // Task hasn't yet fired; wait if (!taskFired) queue.wait(executionTime - currentTime); } //如果任务的执行时间到了,就执行这个任务 if (taskFired) task.run(); } catch(InterruptedException e) { } } } }
我们已经大概知道TimerThread是怎么循环执行定时任务的了,接下来看看Timer的schedule()
以及sched()
方法
schedule & sched
public void schedule(TimerTask task, long delay, long period) { if (delay < 0) throw new IllegalArgumentException("Negative delay."); if (period <= 0) throw new IllegalArgumentException("Non-positive period."); sched(task, System.currentTimeMillis()+delay, -period); } private void sched(TimerTask task, long time, long period) { if (time < 0) throw new IllegalArgumentException("Illegal execution time."); // 获取执行周期 period 的绝对值,如果大于 二分之一的 Long.MAX_VALUE, 则将 period 除以二 if (Math.abs(period) > (Long.MAX_VALUE >> 1)) period >>= 1; synchronized(queue) { if (!thread.newTasksMayBeScheduled) throw new IllegalStateException("Timer already cancelled."); synchronized(task.lock) { if (task.state != TimerTask.VIRGIN) throw new IllegalStateException( "Task already scheduled or cancelled"); task.nextExecutionTime = time; task.period = period; task.state = TimerTask.SCHEDULED; } // 放入队列中 queue.add(task); // 如果任务加入队列后排在堆顶,则直接唤醒线程执行任务 (还记得之前TimerThread中让线程等待吗?) if (queue.getMin() == task) queue.notify(); } }
3、总结
在我们创建Timer时候就会初始化并启动一条线程,用来执行任务,并初始化一个优先队列,使用的是最小堆数据结构,将最早执行的任务放在堆顶。
当我们调用 schedule() 方法时候,就是将任务放进优先队列中。TimerThread循环判断是否已经达到执行的时间,如果到了先计算出下次执行的时间以及调整堆,最后在执行任务,如果没到则线程进入等待。
这篇关于Java定时任务 - Timer 原理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-31全网首发第二弹!软考2024年5月《软件设计师》真题+解析+答案!(11-20题)
- 2024-05-31全网首发!软考2024年5月《软件设计师》真题+解析+答案!(21-30题)
- 2024-05-30【Java】百万数据excel导出功能如何实现
- 2024-05-30我们小公司,哪像华为一样,用得上IPD(集成产品开发)?
- 2024-05-30java excel上传--poi
- 2024-05-30安装笔记本应用商店的pycharm,再安排pandas等模块,说是没有打包工具?
- 2024-05-29java11新特性
- 2024-05-29哪些无用敏捷指标正在破坏敏捷转型?
- 2024-05-29鸿蒙原生应用再新丁!新华社 入局鸿蒙
- 2024-05-29设计模式 之 迭代器模式(Iterator)