Java Timer&TimerTask原理分析-转载
如果你使用Java语言进行开发,对于定时执行任务这样的需求,自然而然会想到使用Timer和TimerTask完成任务,我最近就使用 Timer和TimerTask完成了一个定时执行的任务,实现得没有问题,但当在TimerTaks的run()方法中使用 Thread.sleep()方式时,可能会出现奇怪的现象,好像Timer失效了,网上查了一下,倒是有人遇到了相同的问题,但是并没有找到一篇解释为什么会出现这种情况,期待有某位达人能够分析清楚这个问题。
遇到了这样的问题,始终让我不爽,于是看了一下Timer的源码,先将了解到的内容整理如下,接下来再看看Thread.sleep()的源码,看能否找到问题所在。
在Java中,与定时任务执行相关的类很少,只有Timer、TimerTask、TimerThread、TaskQueue几个,其中每个类的职责大致如下:
Timer:一个Task的调度类,和TimerTask一样,暴露给最终用户使用的类,通过schedule方法安排Task的执行计划。该类通过TaskQueue和TimerThread类完成Task的调度。
TimerTask:实现Runnable接口,表明每一个任务均为一个独立的线程。通过run()方法提供用户定制自己任务。该类有一个比较重要的成员变量nextExecutionTime ,表示下一次执行该任务的时间。以后会看到,Timer机制就是靠这个值安排Task执行的。
TimerThread:继承于Thread,是真正执行Task的类。
TaskQueue:一个存储Task的数据结构,内部由一个最小堆实现,堆的每个成员为一个TimeTask,每个Task依靠其 nextExecutionTime值进行排序,也就是说,nextExecutionTime最小的任务在队列的最前端,从而能够现实最早执行。
要想使用Timer,用户只需要了解Timer和TimerTask,下面现已一个最基本的Timer和TimerTask使用案例入手,来看一下Timer内部的实现原理。
01 |
import java.util.Timer;
|
02 |
03 |
import java.util.TimerTask;
|
04 |
05 |
import org.junit.Test;
|
06 |
07 |
|
08 |
09 |
class TestTimerTask extends TimerTask {
|
10 |
11 |
@Override
|
12 |
13 |
public void run() {
|
14 |
15 |
System.out.println( "TestTimerTask is running......" );
|
16 |
17 |
}
|
18 |
19 |
} |
20 |
21 |
public class TimerTaskTest {
|
22 |
23 |
@Test
|
24 |
25 |
public void testTimerTask() {
|
26 |
27 |
Timer timer = new Timer();
|
28 |
29 |
timer.schedule( new TestTimerTask(), 0 , 10 );
|
30 |
31 |
}
|
32 |
33 |
} |
上面的代码是一个典型的Timer&TimerTask的应用,下面先来看一下new Timer()干了什么事,其源码如下:
public Timer(String name) {
thread.setName(name); //thread为TimerThread实例。
thread.start();
}
从上面的源代码可以知道,创建Timer对象的同时也启动了TimerThread线程。下面来看看TimerThread干了什么事:
01 |
public void run() {
|
02 |
03 |
try {
|
04 |
05 |
mainLoop(); //线程真正执行的代码在这个私有方法中
|
06 |
07 |
} finally {
|
08 |
09 |
// Someone killed this Thread, behave as if Timer cancelled
|
10 |
11 |
synchronized (queue) {
|
12 |
13 |
newTasksMayBeScheduled = false ;
|
14 |
15 |
queue.clear(); // Eliminate obsolete references
|
16 |
17 |
}
|
18 |
19 |
}
|
20 |
21 |
} |
接着来看看私有方法mainLoop()干了什么事:
01 |
private void mainLoop() {
|
02 |
03 |
while ( true ) {
|
04 |
05 |
try {
|
06 |
07 |
TimerTask task;
|
08 |
09 |
boolean taskFired; //是否已经到达Task的执行时间,如果已经到达,设置为true,否则置为false
|
10 |
11 |
synchronized (queue) {
|
12 |
13 |
// Wait for queue to become non-empty
|
14 |
15 |
while (queue.isEmpty() && newTasksMayBeScheduled)
|
16 |
17 |
queue.wait(); //由此可以看出,Timer通过wait & notify 方法安排线程之间的同步
|
18 |
19 |
if (queue.isEmpty())
|
20 |
21 |
break ; // Queue is empty and will forever remain; die
|
22 |
23 |
|
24 |
25 |
// Queue nonempty; look at first evt and do the right thing
|
26 |
27 |
long currentTime, executionTime;
|
28 |
29 |
task = queue.getMin();
|
30 |
31 |
synchronized (task.lock) {
|
32 |
33 |
if (task.state == TimerTask.CANCELLED) {
|
34 |
35 |
queue.removeMin();
|
36 |
37 |
continue ; // No action required, poll queue again
|
38 |
39 |
}
|
40 |
41 |
currentTime = System.currentTimeMillis();
|
42 |
43 |
executionTime = task.nextExecutionTime;
|
44 |
45 |
if (taskFired = (executionTime<=currentTime)) { //Task的执行时间已到,设置taskFired为true
|
46 |
47 |
if (task.period == 0 ) { // Non-repeating, remove
|
48 |
49 |
queue.removeMin(); //移除队列中的当前任务
|
50 |
51 |
task.state = TimerTask.EXECUTED;
|
52 |
53 |
} else { // Repeating task, reschedule
|
54 |
55 |
queue.rescheduleMin( //重新设置任务的下一次执行时间
|
56 |
57 |
task.period< 0 ? currentTime - task.period
|
58 |
59 |
: executionTime + task.period);
|
60 |
61 |
}
|
62 |
63 |
}
|
64 |
65 |
}
|
66 |
67 |
if (!taskFired) // Task hasn't yet fired; wait
|
68 |
69 |
queue.wait(executionTime - currentTime); //还没有执行时间,通过wait等待特定时间
|
70 |
71 |
}
|
72 |
73 |
if (taskFired) // Task fired; run it, holding no locks
|
74 |
75 |
task.run(); //已经到达执行时间,执行任务
|
76 |
77 |
} catch (InterruptedException e) {
|
78 |
79 |
}
|
80 |
81 |
}
|
82 |
83 |
} |
也就是说,一旦创建了Timer类的实例,就一直存在一个循环在遍历queue中的任务,如果有任务的话,就通过thread去执行该任务,否则线程通过wait()方法阻塞自己,由于没有任务在队列中,就没有必要继续thread中的循环。
上面提到,如果Timer的任务队列中不包含任务时,Timer中的TimerThread线程并不会执行,接着来看看为Timer添加任务后会出现怎样的情况。为Timer添加任务就是timer.schedule()干的事,schedule()方法直接调用Timer的私有方法 sched(),sched()是真正安排Task的地方,其源代码如下:
01 |
private void sched(TimerTask task, long time, long period) {
|
02 |
03 |
if (time < 0 )
|
04 |
05 |
throw new IllegalArgumentException( "Illegal execution time." );
|
06 |
07 |
|
08 |
09 |
synchronized (queue) {
|
10 |
11 |
if (!thread.newTasksMayBeScheduled)
|
12 |
13 |
throw new IllegalStateException( "Timer already cancelled." );
|
14 |
15 |
|
16 |
17 |
synchronized (task.lock) {
|
18 |
19 |
if (task.state != TimerTask.VIRGIN) //我喜欢virgin状态,其他状态表明该Task已经被schedule过了
|
20 |
21 |
throw new IllegalStateException(
|
22 |
23 |
"Task already scheduled or cancelled" );
|
24 |
25 |
|
26 |
27 |
//设置Task下一次应该执行的时间, 由System.currentTimeMillis()+/-delay得到
|
28 |
29 |
task.nextExecutionTime = time;
|
30 |
31 |
task.period = period;
|
32 |
33 |
task.state = TimerTask.SCHEDULED;
|
34 |
35 |
}
|
36 |
37 |
|
38 |
39 |
queue.add(task); //queue为TaskQueue类的实例,添加任务到队列中
|
40 |
41 |
if (queue.getMin() == task) //获取队列中nextExecutionTime最小的任务,如果与当前任务相同
|
42 |
43 |
queue.notify(); //还记得前面看到的queue.wait()方法么
|
44 |
45 |
}
|
46 |
47 |
} |
不要奇怪,为什么要判断queue.getMin() == task时,才通过queue.notify()恢复执行。因为这种方式已经满足所有的唤醒要求了。
如果安排当前Task之前queue为空,显然上述判断为true,于是mainLoop()方法能够继续执行。
如果安排当前Task之前queue不为空,那么mainLoop()方法不会一直被阻塞,不需要notify方法调用。
调用该方法还有一个好处是,如果当前安排的Task的下一次执行时间比queue中其余Task的下一次执行时间都要小,通过notify方法可以提前打开queue.wait(executionTime - currentTime)方法对mainLoop()照成的阻塞,从而使得当前任务能够被优先执行,有点抢占的味道。
上述分析可以看出,Java中Timer机制的实现仅仅使用了JDK中的方法,通过wait & notify机制实现,其源代码也非常简单,但可以想到的是这种实现机制会对开发者造成一种困扰,sched()方法中可以看出,对于一个重复执行的任务,Timer的实现机制是先安排Task下一次执行的时间,然后再启动Task的执行,如果Task的执行时间大于下一次执行的间隔时间,可能出现不可预期的错误。当然,了解了Timer的实现原理,修改这种实现方式也就非常简单了。
相关推荐
通过阅读`TimerTest1.java`和`TimerTest2.java`的代码,你可以进一步了解如何在实际项目中使用`Timer`和`TimerTask`来实现特定的定时任务需求。这两个测试类可能包含了创建`Timer`实例、创建`TimerTask`实例以及调度...
通过分析这个示例,你可以更好地理解`Timer`的工作原理和使用方式。 综上所述,`Timer`和`TimerTask`是Java ME中实现定时功能的关键组件。通过创建和调度`TimerTask`,开发者可以在指定的时间间隔内执行特定操作,...
通过分析提供的代码片段,我们将深入探讨其工作原理、关键组件及其在实际开发中的应用。 #### 二、`java.util.Timer`类简介 `java.util.Timer`是Java标准库中用于调度执行线程的类。它可以安排任务在将来某个时间...
Java提供了`java.util.Timer`类来实现定时器功能,通过创建`Timer`实例并结合`TimerTask`子类,可以轻松实现定时任务的调度。 #### 二、定时器类(`Timer`) 在Java中,`Timer`类是核心组件,用于调度定时任务。它...
`Timer`类是Java提供的一个非线程安全的调度工具,它不直接创建新的线程,而是通过`TimerTask`来安排任务在特定的时间点或以特定的间隔执行。`TimerTask`是`Runnable`的子类,用于封装要执行的任务。我们先来看一下...
下面详细介绍`Timer`和`TimerTask`的工作原理: 1. **创建Timer对象**: 创建一个`Timer`对象,这标志着定时器的启动。例如: ```java Timer timer = new Timer(); ``` 2. **定义TimerTask**: 创建`...
本文将深入探讨Java定时器的原理与应用,通过实例演示如何使用`java.util.Timer`和`java.util.TimerTask`类来创建和管理定时任务。 #### 二、Java定时器基础 ##### 1. `java.util.Timer`类 `Timer`类位于`java....
为了使时钟持续更新,你需要使用java.util.Timer和TimerTask类。定时器会在指定间隔执行任务,这里的任务就是更新时间标签的文本。每次触发时,获取系统当前时间(通过java.time包或java.util.Calendar),并将时间...
- **TimerTask原理**:深入理解 `TimerTask` 的工作原理,以及如何利用其进行定时任务开发。 - **多线程编程**:学习Android中多线程编程的相关知识,例如 `Handler`、`Looper`、`Thread` 等。 #### 四、总结 本文...
在Android开发中,`Timer...总之,`Timer`和`TimerTask`是Java中强大的工具,但它们的使用需谨慎,尤其是在Android环境下。理解它们的工作原理和限制,结合具体场景选择合适的调度策略,可以提高应用的性能和用户体验。
例如,Java中的`java.util.Timer`类和`java.util.TimerTask`接口,它们可以用来定期执行任务。在Python中,我们可以使用`threading.Timer`来实现定时功能。C++则通常依赖于操作系统提供的定时器API,如POSIX的`alarm...
开发者可能利用Java的定时器API(如java.util.Timer和TimerTask)来周期性地更新时间显示,模拟单片机的实时性。 在提供的压缩包中,`clock.jar`是一个可执行的Java应用程序,用户可以直接运行来看到点阵电子钟的...
`java.util.Timer` 是Java早期提供的定时器,它通过`TimerTask`类来定义需要定时执行的任务。创建一个`Timer`实例后,可以调用`schedule(TimerTask task, long delay)`方法来延迟一段时间后执行任务,或者`schedule...
Java定时器的实现原理涉及到以下几个关键知识点: 1. **线程基础**:Java的定时器是基于线程的,它会创建一个后台线程来执行任务。理解线程的概念、线程安全以及如何避免线程间的竞态条件是必要的。 2. **时间管理...
本资料包“安卓天气日历时间选择倒计时相关-Button倒计时用到了Timer计时器和Handler.zip”主要关注如何在Android应用中实现Button的倒计时功能,该功能通常涉及到两个关键组件:`java.util.Timer` 和 `android.os....
首先,Java提供了一个内置的定时框架——`java.util.Timer`类和`TimerTask`类,它们可以用来创建简单的定时任务。`Timer`类用于调度任务,而`TimerTask`是执行的任务对象。以下是一个简单的示例: ```java import ...
#### 二、算法原理与分析 ##### 2.1 算法原理 本示例中采用的是一个较为简单的求素数方法:遍历所有小于等于某个上限(在这里是`MAX_PRIMES`)的整数,并检查每个数是否为素数。如果一个数不能被之前找到的任何...
`java.util.Timer`类负责管理一系列`TimerTask`实例,并在特定的时间点执行这些任务。用户可以通过调用`Timer`类的方法来安排任务的执行,这些方法主要包括: - `schedule(TimerTask task, long delay, long period)...