Java多线程编程核心技术-5-Timer的使用

定时器Timer的使用

Timer类

Timer类的主要作用是设置计划任务,即在指定时间开始执行某一个任务。

其构造方法如下

1
2
3
4
5
6
7
public Timer() {
this("Timer-" + serialNumber());
}

public Timer(boolean isDaemon) {
this("Timer-" + serialNumber(), isDaemon);
}

即可以自定义该定时器的名称。

其主要有四个方法:

  • schedule
  • scheduleAtFixedRate
  • cancel
  • purge

其中第一二个都是计划执行的方法;而第三个为取消任务;第四个为移除所有被取消的任务,并返回器数量。

TimerTask类

TimerTask是实现了Runnable接口的一个抽象类,即实际上是封装了一个线程。

主要是在内部添加了状态属性:

1
2
3
4
5
int state = VIRGIN;
static final int VIRGIN = 0;
static final int SCHEDULED = 1;
static final int EXECUTED = 2;
static final int CANCELLED = 3;

并且新建了一个空对象作为锁对象。

1
2
3
4
/**
* This object is used to control access to the TimerTask internals.
*/
final Object lock = new Object();

我们需要去继承TimerTask,然后重写run方法,进而实现自己的任务。

单次任务

下面介绍可以单词执行任务的方法。

指定时间执行-schedule(TimerTask task, Date time)

可以通过schedule(TimerTask task,Date time)来执行定时只执行一次的任务。

例如10秒后打印字符串:

TimerTask类:

1
2
3
4
5
6
7
8
9
10
11
12
13
package mw;

import java.util.TimerTask;

public class MyTimerTask extends TimerTask{

@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("task begin!");
}

}

执行类:

1
2
3
4
5
6
7
8
9
10
11
12
13
package mw;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Main {
public static void main(String[] args) {
Timer timer = new Timer();
TimerTask timerTask = new MyTimerTask();
timer.schedule(timerTask, new Date(System.currentTimeMillis()+10000));
}
}

TimerThread线程不会自动销毁

执行后会发现一个问题,即使当定时器已经执行完毕,定时器线程并不会结束。这是因为任务的实际执行是在Timer类内部的TimerThread线程来执行。其run方法是一个死循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public void run() {
try {
mainLoop();
} finally {
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) {
newTasksMayBeScheduled = false;
queue.clear(); // Eliminate obsolete references
}
}
}

/**
* 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 = 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);
}
}
}
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) {
}
}
}

所以即使是定时器全部结束,也不会停止线程。

而我们可以使用public void cancel()来手动使TimerThread终止。

Timer类中public void cancel()方法的作用是终止此计时器,丢弃所有当前已安排的任务。这不会干扰当前正在执行的任务(如果存在)。一旦终止了计时器,那么它的执行线程也会终止,并且无法根据它安排更多的任务。注意,在此计时器调用的计时器任务的run()方法内调用此方法,可以确保正在执行的任务是此计时器所执行的最后一个任务。可以重复调用此方法,但是第二次和后续调用无效。

例如:

执行类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package mw;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Main {
public static void main(String[] args) throws InterruptedException {
Timer timer = new Timer();
TimerTask timerTask = new MyTimerTask();
timer.schedule(timerTask, new Date(System.currentTimeMillis()+10000));
Thread.sleep(15000);
timer.cancel();
}
}

即指定10秒钟后执行该任务,15秒后取消该定时器,定时线程也被终止。

计划时间早于当前时间——立即运行的效果

如果指定的时间在当前时间之前,则该任务会立即执行。但是注意,其仍然是在异步线程中使用,所以仍然会比主线程慢输出,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package mw;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Main {
public static void main(String[] args) throws InterruptedException {
Timer timer = new Timer();
TimerTask timerTask = new MyTimerTask();
timer.schedule(timerTask, new Date(System.currentTimeMillis()-10000));
System.out.println("main thread!");
timer.cancel();
}
}

其结果为:

1
2
main thread!
task begin!

一个Timer中执行多个TimerTask任务

一个Timer相当于一个管理器,因此其可以管理多个异步任务。只需要多次调用schedule方法即可。

例如:

任务类:

1
2
3
4
5
6
7
8
9
10
11
12
13
package mw;

import java.util.TimerTask;

public class MyTimerTask extends TimerTask{

@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Task Time: " + System.currentTimeMillis());
}

}

执行类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package mw;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Main {
public static void main(String[] args) throws InterruptedException {
long nowTime = System.currentTimeMillis();
System.out.println("current time: " + nowTime);

long scheduleTime1 = (nowTime + 5000);
long scheduleTime2 = (nowTime + 8000);
System.out.println("task1 time: " + scheduleTime1);
System.out.println("task2 time: " + scheduleTime2);

MyTimerTask task1 = new MyTimerTask();
MyTimerTask task2 = new MyTimerTask();

Timer timer = new Timer();
timer.schedule(task1, new Date(scheduleTime1));
timer.schedule(task2, new Date(scheduleTime2));

System.out.println("main thread!");
}
}

结果:

1
2
3
4
5
6
current time: 1649239031782
task1 time: 1649239036782
task2 time: 1649239039782
main thread!
Task Time: 1649239036782
Task Time: 1649239039796

可以看到其都执行了。

指定时间间隔后执行-schedule(TimerTask task,long delay)

该方法与上述的schedule(TimerTask task, Date time)都是添加一个任务。

不过该方法是以执行方法当前的时间为参考时间,在此时间基础上延迟指定的毫秒数后执行一次TimerTask任务。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package test;

import java.util.Timer;
import java.util.TimerTask;

public class Run {
static public class MyTask extends TimerTask {
@Override
public void run() {
System.out.println("运行了!时间为:" + System.currentTimeMillis());
}
}

public static void main(String[] args) {
MyTask task = new MyTask();
Timer timer = new Timer();
System.out.println("当前时间:" + System.currentTimeMillis());
timer.schedule(task, 7000);
}
}

循环任务

还可以制定以一定间隔一直循环执行任务。

追赶任务

指定第一次任务-schedule(TimerTask task,Date firstTime,long period)

该方法的作用是在指定日期之后按指定的间隔周期无限循环地执行某一任务。

例如,执行类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package test;

import java.util.Date;
import java.util.Timer;

import mytask.MyTask;

public class Test1 {

public static void main(String[] args) {
long nowTime = System.currentTimeMillis();
System.out.println("当前时间为:" + nowTime);

long scheduleTime = (nowTime + 10000);
System.out.println("计划时间为:" + scheduleTime);

MyTask task = new MyTask();

Timer timer = new Timer();
timer.schedule(task, new Date(scheduleTime), 4000);
}

}

结果:

1
2
3
4
5
6
current time: 1649241014210
plan time: 1649241024210
Task Time: 1649241024210
Task Time: 1649241028219
Task Time: 1649241032229
Task Time: 1649241036244

以当前时间为第一次任务的开始时间-schedule(TimerTask task,long delay,long period)

该方法的作用是以执行schedule(TimerTask task,long delay,long period)方法当前的时间为参考时间,在此时间基础上延迟指定的毫秒数后执行一次TimerTask任务。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package test;

import java.util.Timer;
import java.util.TimerTask;

public class Run {
static public class MyTask extends TimerTask {
@Override
public void run() {
System.out.println("运行了!时间为:" + System.currentTimeMillis());
}
}

public static void main(String[] args) {
MyTask task = new MyTask();
Timer timer = new Timer();
System.out.println("当前时间:" + System.currentTimeMillis());
timer.schedule(task, 7000);
}
}

结果:

1
2
3
4
5
current time: 1649241296832
plan time: 1649241306832
Task Time: 1649241299845
Task Time: 1649241300855
Task Time: 1649241301870

不追赶任务-scheduleAtFixedRate

追赶测试

schedule()方法和scheduleAtFixedRate()方法的主要区别在于有没有追赶特性。

所谓追赶,即当上一次任务的结束时间已经比该次任务的计划时间晚了,那么该次任务是否还执行。

而schedule是不具有追赶行的,即过期的任务不会被执行。

scheduleAtFixedRate具有追赶性,即使任务过期,还是会执行。

例如:

追赶执行类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test9 {
static class MyTask extends TimerTask {
public void run() {
System.out.println("begin timer=" + System.currentTimeMillis());
System.out.println(" end timer=" + System.currentTimeMillis());
}
}

public static void main(String[] args) {
MyTask task = new MyTask();
long nowTime = System.currentTimeMillis();
System.out.println("现在执行时间:" + nowTime);
long runTime = nowTime - 20000;
System.out.println("计划执行时间:" + runTime);
Timer timer = new Timer();
timer.schedule(task, new Date(runTime), 2000);
}
}

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
current time: 1649242106556
plan time: 1649242086556
begin timer=1649242106557
end timer=1649242106557
begin timer=1649242108563
end timer=1649242108563
begin timer=1649242110575
end timer=1649242110575
begin timer=1649242112582
end timer=1649242112582
begin timer=1649242114596
end timer=1649242114596
begin timer=1649242116607
end timer=1649242116607
begin timer=1649242118610
end timer=1649242118610
begin timer=1649242120622
end timer=1649242120622
begin timer=1649242122633
end timer=1649242122633

可以看到,1649242086556到1649242106556之间的任务都没有被执行。

不追赶测试

同样的,我们将其换成scheduleAtFixedRate方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package mw;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Test1 {
static class MyTask extends TimerTask {
public void run() {
System.out.println("begin timer=" + System.currentTimeMillis());
System.out.println(" end timer=" + System.currentTimeMillis());
}
}

public static void main(String[] args) {
MyTask task = new MyTask();
long nowTime = System.currentTimeMillis();
System.out.println("current time: " + nowTime);
long runTime = nowTime - 20000;
System.out.println("plan time: " + runTime);
Timer timer = new Timer();
timer.scheduleAtFixedRate(task, new Date(runTime), 2000);
}
}

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
current time: 1649242326064
plan time: 1649242306064
begin timer=1649242326066
end timer=1649242326066
begin timer=1649242326066
end timer=1649242326066
begin timer=1649242326066
end timer=1649242326066
begin timer=1649242326066
end timer=1649242326066
begin timer=1649242326066
end timer=1649242326066
begin timer=1649242326066
end timer=1649242326066
begin timer=1649242326066
end timer=1649242326066
begin timer=1649242326066
end timer=1649242326066
begin timer=1649242326066
end timer=1649242326066
begin timer=1649242326066
end timer=1649242326066
begin timer=1649242326066
end timer=1649242326066
begin timer=1649242328064
end timer=1649242328064
begin timer=1649242330070
end timer=1649242330070
begin timer=1649242332064
end timer=1649242332064
begin timer=1649242334077
end timer=1649242334077
begin timer=1649242336065
end timer=1649242336065
begin timer=1649242338065
end timer=1649242338065

可以看到,1649242326066的时间都被追赶执行。将20s之内执行任务的次数输出完,再每间隔2s执行一次任务。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2019 - 2024 My Wonderland All Rights Reserved.

UV : | PV :