在Java开发中,java.util.Timer是常用的定时任务调度工具,它通过一个后台线程来执行被安排的任务。然而,如果Timer使用完毕后没有正确停止,其后台线程会一直存活,导致资源泄漏甚至JVM无法正常退出。本文将详细讲解三种停止Timer的核心方法,并附上完整代码示例和注意事项。
cancel()是Timer类提供的最直接、最常用的停止方法。调用后,Timer会丢弃所有已安排的任务,终止后台工作线程,且之后不能再向该Timer安排任何新任务。
import java.util.Timer;
import java.util.TimerTask;
public class TimerCancelDemo {
public static void main(String[] args) throws InterruptedException {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println("任务执行中... " + System.currentTimeMillis());
}
}, 0, 1000); // 每秒执行一次
// 主线程等待5秒后停止定时器
Thread.sleep(5000);
timer.cancel();
System.out.println("定时器已终止");
}
}技术要点:cancel()方法不会中断正在执行中的任务,而是等待当前任务执行完毕后再终止线程。Timer被cancel后不可复用,如需重新调度任务必须创建新的Timer实例。此方法会终止该Timer下的所有任务,适合需要一次性彻底关闭定时器的场景。
如果只需要停止某个特定任务而保留Timer继续运行其他任务,可以调用TimerTask实例的cancel()方法。该方法仅取消当前任务,不影响Timer中的其他任务。
import java.util.Timer;
import java.util.TimerTask;
public class TaskCancelDemo {
public static void main(String[] args) throws InterruptedException {
Timer timer = new Timer();
TimerTask taskA = new TimerTask() {
@Override
public void run() {
System.out.println("任务A执行中...");
}
};
TimerTask taskB = new TimerTask() {
@Override
public void run() {
System.out.println("任务B执行中...");
}
};
timer.scheduleAtFixedRate(taskA, 0, 1000);
timer.scheduleAtFixedRate(taskB, 0, 2000);
// 3秒后仅取消任务A,任务B继续执行
Thread.sleep(3000);
taskA.cancel();
System.out.println("任务A已取消,任务B继续运行");
// 再等5秒后终止整个Timer
Thread.sleep(5000);
timer.cancel();
}
}技术要点:被取消的任务仍会保留在Timer的任务队列中,占用少量内存。如果频繁取消大量任务,应配合调用timer.purge()方法来清除队列中已取消的任务,释放内存。purge()方法返回被移除的任务数量。
前两种方法都是"强制"停止,而通过标志位控制是一种更优雅、更灵活的方式。在任务的run()方法中检查一个volatile布尔标志,当标志变为false时,任务自行退出循环或调用this.cancel()。
import java.util.Timer;
import java.util.TimerTask;
public class FlagControlDemo {
private static volatile boolean running = true;
public static void main(String[] args) throws InterruptedException {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (running) {
System.out.println("任务执行中... " + System.currentTimeMillis());
} else {
System.out.println("收到停止信号,任务退出");
this.cancel();
}
}
}, 0, 1000);
// 5秒后将标志位设为false,触发任务自行停止
Thread.sleep(5000);
running = false;
System.out.println("已发送停止信号");
// 等待任务完成退出后再终止Timer
Thread.sleep(2000);
timer.cancel();
}
}技术要点:标志位必须声明为volatile,确保多线程环境下的可见性,即主线程修改标志后,Timer线程能立即感知到变化。此方式允许任务在停止前完成当前轮次的收尾工作(如关闭连接、刷写缓冲区),实现优雅停机。适合需要精细控制任务生命周期的场景。
Timer不可复用:Timer一旦被cancel(),其后台线程就会终止,不能再安排新任务。如需重新调度,必须创建新的Timer实例。
未捕获异常会杀死Timer线程:如果任务run()方法中抛出了未捕获的运行时异常,Timer的后台线程会静默终止,后续所有任务都不会再执行。务必在run()方法中用try-catch包裹所有业务逻辑。
非daemon Timer会阻止JVM退出:默认创建的Timer是非守护线程,如果程序结束时没有调用cancel(),JVM将无法正常退出。可以通过new Timer(true)创建守护线程模式的Timer,这样当程序中只剩下守护线程时JVM会自动退出。
Timer是单线程模型:所有任务共享一个后台线程串行执行,如果某个任务执行时间过长,会阻塞其他任务的调度。对于任务较重或需要并发执行的场景,建议使用ScheduledExecutorService替代。
生产环境推荐ScheduledExecutorService:ScheduledExecutorService基于线程池实现,支持多线程并发执行、异常隔离(单个任务异常不影响其他任务)、更丰富的取消控制(通过ScheduledFuture.cancel()),是生产环境中的首选方案。
![]()
Java中停止Timer主要有三种核心方法:Timer.cancel()用于一次性终止整个定时器及其所有任务;TimerTask.cancel()用于精确取消单个任务而保留其他任务继续运行;标志位控制则提供了一种优雅停止的机制,允许任务在完成当前轮次后自行退出。在实际开发中,应根据业务需求选择合适的方法,同时注意异常捕获、线程类型设置和资源释放。对于复杂的生产环境,建议优先考虑使用ScheduledExecutorService替代Timer,以获得更好的线程管理能力和容错性。
声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com
通过手机号码查询近3个月总停机次数标签信息,统计近3个月内停机的次数。
通过手机号查询判断该号码实名用户年龄区间标签信息。
通过三网运营商手机号码和指定月份,查询号码近3个月话费消费区间标签详情及评分。
通过车架号或车牌号查询车辆是否为营运车辆
通过车架号查询车辆的如品牌名称、车系名称、车型、排量、排放标准、外形尺寸、轮胎规格、变速器类型、公告号、轴距等等详细信息