守护线程定时清理器需设为daemon、启动前配置、捕获中断并重置状态、清理逻辑幂等无阻塞,确保jvm启停无感。

用守护线程实现一个随 JVM 启动而启动、随 JVM 退出而静默终止的定时清理器,关键不在“多酷”,而在“不打扰”——它不该阻塞主线程,不该抛未捕获异常,不该在 shutdown 时卡住进程,更不该留下残留任务。核心思路是:用 Thread.setDaemon(true) 标记为守护线程 + 单次循环内完成全部逻辑 + 使用 try-catch 包裹整个执行体 + 配合 Thread.sleep() 实现轻量轮询(或用 ScheduledExecutorService 更稳妥,但需确保其线程也是守护态)。
守护线程必须在 start() 前设置
这是硬性规则。一旦线程已启动,再调用 setDaemon(true) 会抛 IllegalThreadStateException。所以初始化要写成:
- 新建 Thread 实例
- 立即调用
thread.setDaemon(true) - 再调用
thread.start()
避免 sleep 中断导致线程意外退出
守护线程若在 sleep() 中被中断(比如 JVM 收到 SIGTERM),会抛 InterruptedException 并清空中断状态。若不处理,线程直接结束,可能错过最后一次清理。正确做法是:
- 捕获
InterruptedException - 重置当前线程中断状态:
Thread.currentThread().interrupt() - 不 throw,不 return,继续下一轮循环(JVM 正在关闭时,下次 sleep 会更快响应)
清理逻辑本身要幂等且无副作用
所谓“无感”,意味着即使同一时刻多个清理任务并发执行(因调度偏差或重启抖动),也不该报错或误删。建议:
- 所有文件操作前加
if (file.exists() && file.isFile())判断 - 用
Files.deleteIfExists(path)替代file.delete(),避免 NoSuchFileException - 清理目录时先递归列出再逐个删除,跳过正在被占用的文件(捕获
IOException后仅 log.warn,不中断流程)
让 JVM 优雅退出时不卡住
守护线程不会阻止 JVM 退出,但若清理逻辑中持有锁、等待网络响应或阻塞 I/O,就可能拖慢 shutdown。因此:
- 禁止在清理器中做 HTTP 调用、数据库查询、长耗时压缩解压
- 所有 I/O 操作设超时(如
Files.walk(path).forEach(...)改为带 try-with-resources 的流式遍历) - 可在 JVM 关闭钩子(
Runtime.getRuntime().addShutdownHook())里触发一次强制清理,但钩子里的线程也应是守护态,且只做最简动作
不复杂但容易忽略。











