timer 不推荐使用,因其单线程设计导致未捕获异常时整个调度器崩溃;schedule 基于上一次执行完成时间顺延,scheduleatfixedrate 基于初始预定时间严格推进;应改用 scheduledexecutorservice。
Java 里 Timer 和 TimerTask 能实现基础定时任务,但不推荐在新项目中使用——它线程不安全、无法处理异常导致后续任务全部停摆、也不支持并发执行。
为什么 Timer 容易“一崩全停”
一个 Timer 实例背后只有一条后台线程(TimerThread),所有 TimerTask 都由它串行执行。一旦某个 TimerTask 的 run() 方法抛出未捕获异常,该线程会立即终止,之后注册的所有任务永远不会再触发。
- 常见错误现象:
Timer突然“静默失效”,控制台无报错,但任务不再执行 - 典型场景:网络请求失败没 try-catch、JSON 解析异常、数据库连接中断
- 无法恢复:线程挂了就挂了,没有重试或重启机制
schedule() 和 scheduleAtFixedRate() 的关键区别
两者都用于周期性调度,但对“延迟”和“执行节奏”的处理逻辑完全不同:
-
schedule(task, delay, period):以“上一次实际执行完成时间”为基准,延迟period后再启动下一次;若某次执行超时,下一次会自动顺延,不会堆积 -
scheduleAtFixedRate(task, firstTime, period):以“预定起始时间”为基准,严格按period间隔推进;若某次执行耗时过长,后续任务可能被压缩甚至并发触发(但Timer是单线程,所以实际是立即排队) - 性能影响:后者更容易因任务延迟引发队列积压,最终
OutOfMemoryError: Java heap space(大量未执行的TimerTask对象滞留在队列中)
如何避免 TimerTask 泄漏和资源未释放
TimerTask 是抽象类,子类常持有外部对象引用(如 Service、DAO、Context),若忘记取消或 Timer 未被回收,会导致 GC 无法清理,引发内存泄漏。