我们有时候会有这样的需求,我希望定时执行任务,比如3秒后执行,其实我们可以通过使用Thread.sleep()
来实现:
public static void main(String[] args) {
new TimerTask(() -> System.out.println("我是定时任务!"), 3000).start(); //创建并启动此定时任务
}
static class TimerTask{
Runnable task;
long time;
public TimerTask(Runnable runnable, long time){
this.task = runnable;
this.time = time;
}
public void start(){
new Thread(() -> {
try {
Thread.sleep(time);
task.run(); //休眠后再运行
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
我们通过自行封装一个TimerTask类,并在启动时,先休眠3秒钟,再执行我们传入的内容。那么现在我们希望,能否循环执行一个任务呢?比如我希望每隔1秒钟执行一次代码,这样该怎么做呢?
public static void main(String[] args) {
new TimerLoopTask(() -> System.out.println("我是定时任务!"), 3000).start(); //创建并启动此定时任务
}
static class TimerLoopTask{
Runnable task;
long loopTime;
public TimerLoopTask(Runnable runnable, long loopTime){
this.task = runnable;
this.loopTime = loopTime;
}
public void start(){
new Thread(() -> {
try {
while (true){ //无限循环执行
Thread.sleep(loopTime);
task.run(); //休眠后再运行
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
现在我们将单次执行放入到一个无限循环中,这样就能一直执行了,并且按照我们的间隔时间进行。
但是终究是我们自己实现,可能很多方面还没考虑到,Java也为我们提供了一套自己的框架用于处理定时任务:
public static void main(String[] args) {
Timer timer = new Timer(); //创建定时器对象
timer.schedule(new TimerTask() { //注意这个是一个抽象类,不是接口,无法使用lambda表达式简化,只能使用匿名内部类
@Override
public void run() {
System.out.println(Thread.currentThread().getName()); //打印当前线程名称
}
}, 1000); //执行一个延时任务
}
我们可以通过创建一个Timer类来让它进行定时任务调度,我们可以通过此对象来创建任意类型的定时任务,包延时任务、循环定时任务等。我们发现,虽然任务执行完成了,但是我们的程序并没有停止,这是因为Timer内存维护了一个任务队列和一个工作线程:
public class Timer {
/**
* The timer task queue. This data structure is shared with the timer
* thread. The timer produces tasks, via its various schedule calls,
* and the timer thread consumes, executing timer tasks as appropriate,
* and removing them from the queue when they're obsolete.
*/
private final TaskQueue queue = new TaskQueue();
/**
* The timer thread.
*/
private final TimerThread thread = new TimerThread(queue);
...
}
TimerThread继承自Thread,是一个新创建的线程,在构造时自动启动:
public Timer(String name) {
thread.setName(name);
thread.start();
}
而它的run方法会循环地读取队列中是否还有任务,如果有任务依次执行,没有的话就暂时处于休眠状态:
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() {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// Wait for queue to become non-empty
while (queue.isEmpty() && newTasksMayBeScheduled) //当队列为空同时没有被关闭时,会调用wait()方法暂时处于等待状态,当有新的任务时,会被唤醒。
queue.wait();
if (queue.isEmpty())
break; //当被唤醒后都没有任务时,就会结束循环,也就是结束工作线程
...
}
newTasksMayBeScheduled
实际上就是标记当前定时器是否关闭,当它为false时,表示已经不会再有新的任务到来,也就是关闭,我们可以通过调用cancel()
方法来关闭它的工作线程:
public void cancel() {
synchronized(queue) {
thread.newTasksMayBeScheduled = false;
queue.clear();
queue.notify(); //唤醒wait使得工作线程结束
}
}
因此,我们可以在使用完成后,调用Timer的cancel()
方法以正常退出我们的程序:
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
timer.cancel(); //结束
}
}, 1000);
}