当前位置: 首页 > news >正文

ScheduledExecutorService

引言

ScheduledExecutorService 是啥?

Java 的定时任务线程池。用它可以:

  • 延时执行一次任务:schedule(...)

  • 按固定频率循环执行:scheduleAtFixedRate(...)

  • 按固定间隔循环执行:scheduleWithFixedDelay(...)

三个核心方法差异

  1. schedule(task, delay, unit)延时一次
     
  2. scheduleAtFixedRate(task, initialDelay, period, unit)按“时钟频率”跑;不等上一个任务结束的时间来对齐下一个开始时间(可能堆积)
     
  3. scheduleWithFixedDelay(task, initialDelay, delay, unit)上一个结束后再等 delay 才开始下一个(不堆积,更稳)

使用

java使用

import java.util.concurrent.*;ScheduledExecutorService scheduler =Executors.newScheduledThreadPool(1); // 常用:单线程定时器// 1) 延时 2 秒执行一次
ScheduledFuture<?> oneShot = scheduler.schedule(() -> {System.out.println("do once");
}, 2, TimeUnit.SECONDS);// 2) 固定频率:1 秒后启动,每 500ms 触发(可能堆积)
ScheduledFuture<?> fixedRate = scheduler.scheduleAtFixedRate(() -> {try {doWork();} catch (Exception e) {e.printStackTrace(); // 一定要捕获异常,否则周期任务会停止}
}, 1, 500, TimeUnit.MILLISECONDS);// 3) 固定间隔:1 秒后启动,每次结束后隔 500ms 再下一次
ScheduledFuture<?> fixedDelay = scheduler.scheduleWithFixedDelay(() -> {try {doWork();} catch (Exception e) {e.printStackTrace();}
}, 1, 500, TimeUnit.MILLISECONDS);// 取消任务
fixedRate.cancel(false); // 参数 true 则中断正在执行的线程// 退出线程池
scheduler.shutdown(); // 或者 shutdownNow();

Kotlin(Android 常用)

val scheduler: ScheduledExecutorService = Executors.newScheduledThreadPool(1)// 延时一次
val future = scheduler.schedule({// do once
}, 2, TimeUnit.SECONDS)// 固定间隔(更常用,避免堆积)
val periodic = scheduler.scheduleWithFixedDelay({try {// your periodic work} catch (t: Throwable) {t.printStackTrace()}
}, 1, 500, TimeUnit.MILLISECONDS)// 取消
periodic.cancel(false)// 关闭
scheduler.shutdown()

实践

  • 需要精准频率(如心跳/采样) → scheduleAtFixedRate

  • 任务耗时不稳定/不能堆积 → scheduleWithFixedDelay(大多数业务更安全)

  • 只执行一次 → schedule

常见坑 & 最佳实践

  1. 一定捕获异常:周期任务里抛出未捕获异常会让该周期任务直接停止。
     
  2. 不要长时间阻塞定时线程:如果任务很慢,用线程池执行实际工作,定时器只负责“触发”。
     
  3. 合理线程数newScheduledThreadPool(1)足够多数场景;需要并行周期任务时再加大线程数。
     
  4. 取消要清理:保存 ScheduledFuture<?>,在 onStop()/onDestroy() 里 cancel(),再 shutdown()
     
  5. Android UI:它跑在后台线程,不能直接操作 UI;需要切回主线程(Handler/runOnUiThread/LiveData/Flow)。
     
  6. Android 替代:简单周期任务可用 Kotlin 协程
  7. scope.launch {delay(1000)while (isActive) {doWork()delay(500) // 等价于 fixedDelay}
    }
    

    需要约束(充电/Wi-Fi/重启恢复)请选择 WorkManager

项目里可以这样用(语音/电机轮询示例)

class MotorAnglePoller {private val scheduler = Executors.newScheduledThreadPool(1)private var future: ScheduledFuture<*>? = nullfun start() {if (future?.isCancelled == false) returnfuture = scheduler.scheduleWithFixedDelay({try {val motorAngle = readMotorAngle()     // 读取电机角度val faceOffset = latestFaceOffset()   // 读共享状态adjustMotor(motorAngle, faceOffset)   // 计算并发指令} catch (t: Throwable) {t.printStackTrace()}}, 0, 60, TimeUnit.MILLISECONDS) // 约 ~16Hz 轮询}fun stop() {future?.cancel(false)future = null}fun release() {stop()scheduler.shutdown()}
}

什么时候不用它?

  • 需要与生命周期绑紧、UI 线程切换频繁 → 协程 + Lifecycle 更顺手。

  • 需要设备重启后继续、网络/充电条件 → WorkManager

scheduleAtFixedRate 堆积问题

固定频率:1 秒后启动,每 500ms 触发(可能堆积)
ScheduledFuture<?> fixedRate = scheduler.scheduleAtFixedRate(() -> {
try { doWork();
} catch (Exception e) {
e.printStackTrace(); // 一定要捕获异常,否则周期任务会停止 }

}, 1, 500, TimeUnit.MILLISECONDS);

“堆积”,本质是:scheduleAtFixedRate 按时钟对齐触发;如果某次 doWork() 比周期(500ms)更慢,调度器会立刻补跑落下的触发(出现“连着跑几次”的感觉)。解决有几条路,按常见程度给你三种“防堆积”方案:

方案 A(最简单):改用固定间隔

把 scheduleAtFixedRate 换成 scheduleWithFixedDelay,上一轮结束后再等 500ms,天然不堆积。

ScheduledFuture<?> task = scheduler.scheduleWithFixedDelay(() -> {try {doWork();} catch (Exception e) {e.printStackTrace();}
}, 1, 500, TimeUnit.MILLISECONDS);

适用:允许频率略有波动、但坚决不能补跑/堆积 的场景(大多数业务都适用)。

方案 B(保持固定频率,但忙就跳过

维持 scheduleAtFixedRate 的“时钟对齐”语义,但如果上一轮还在跑,就直接跳过这次,避免补跑。

import java.util.concurrent.atomic.AtomicBoolean;AtomicBoolean running = new AtomicBoolean(false);ScheduledFuture<?> fixedRate = scheduler.scheduleAtFixedRate(() -> {if (!running.compareAndSet(false, true)) {// 上一轮还没结束 → 跳过当前 tickreturn;}try {doWork();} catch (Exception e) {e.printStackTrace();} finally {running.set(false);}
}, 1, 500, TimeUnit.MILLISECONDS);

想“忙完后至少再补一次”(不丢掉最后一次触发),加一个 pending 标志:

AtomicBoolean running = new AtomicBoolean(false);
AtomicBoolean pending = new AtomicBoolean(false);scheduler.scheduleAtFixedRate(() -> {if (!running.compareAndSet(false, true)) {pending.set(true); // 记一笔:有一次触发被跳过return;}try {doWork();// 如果期间有人标记了 pending,则立刻再跑一次(只补一轮)if (pending.getAndSet(false)) {doWork();}} catch (Exception e) {e.printStackTrace();} finally {running.set(false);}
}, 1, 500, TimeUnit.MILLISECONDS);

适用:要对齐节拍,但不能排队的场景(例如传感器处理、UI 心跳)。

方案 C(背压合并:只保留“最新一次”)

定时器只负责“投递”工作请求到一个容量=1的队列;真正的工作在单独线程里执行。队列满就覆盖/丢旧,只保留最新,天然不堆积。

import java.util.concurrent.*;ExecutorService worker = Executors.newSingleThreadExecutor();
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1);// 单独的消费者一直处理
worker.execute(() -> {for (;;) {try {Runnable job = queue.take();job.run();} catch (InterruptedException e) {Thread.currentThread().interrupt();break;} catch (Throwable t) {t.printStackTrace();}}
});// 定时触发:尝试把“最新任务”放进队列(满了就丢旧换新)
scheduler.scheduleAtFixedRate(() -> {Runnable job = () -> {try { doWork(); } catch (Exception e) { e.printStackTrace(); }};// 清掉旧的,只保留这次(“只留最新”)queue.clear();queue.offer(job);
}, 1, 500, TimeUnit.MILLISECONDS);

适用:生产快、消费慢,但你只关心最新状态(如状态刷新、数据快照)的场景。

选型建议

  • 业务允许“间隔从完成时刻开始计时” → 方案 A(最简单、最稳)。

  • 必须“按时钟节拍”但不能补跑 → 方案 B(跳过或“只补一次”)。

  • 数据是最新优先,不需要处理历史 backlog → 方案 C(合并+背压)。

无论哪种方案,记得:

  1. 任务里捕获异常,避免周期任务静默停止;

  2. 周期线程池建议单线程或受控并发,防止并发访问共享资源;

  3. 生命周期结束要 cancel() 并 shutdown(),防泄漏。

下一篇:

ScheduledExecutorService vs Timer/TimerTask核心区别

http://www.dtcms.com/a/504493.html

相关文章:

  • 网站首页新世纪建设集团有限公司长沙网站seo收费
  • 网站优化的论文pc网站建设企业
  • 各个视频网站怎么做原创作者南宁市建筑规划设计集团有限公司
  • 探索Word2Vec:从文本向量化到中文语料处理
  • 性能测试 | 认识性能测试的概念以及应用
  • 小企业网站建设论文东莞网站建设设计
  • 第23讲:文件操作
  • 网站如何不需要备案免费发广告的软件有哪些
  • 网站制作 需要什么网络技术唯品会网站建设特色
  • 如何提高网站点击率dns网站建设
  • 响应式布局的原理网站meta 优化建议
  • 网站建设设计收费网络销售面试问题有哪些
  • mysql8支持远程访问 -mysql5.7支持远程访问
  • DeepSeek+FastGPT+Xinferenc打造企业级知识库
  • 营销型网站费用软件工程35岁就失业吗
  • 开发个网站开票名称是什么意思项目网上备案
  • 2025年-集合类面试题
  • 商城网站静态模板下载安徽安庆天气预报15天
  • 网站任务界面wordpress实例网址
  • Python网络编程调用CnOCR文字识别教程
  • 常熟制作网站的地方广州网页制作
  • 青岛做网站价格关键词排名优化公司
  • 第十九周-训练embedding
  • 何为网站开发如何用cms做网站
  • 2022ICPC区域赛济南站
  • 英文网站建设一般多少钱婚纱摄影图片
  • 家具东莞网站建设技术支持wordpress开启多站点后台没显示
  • 大模型应用开发面经
  • python -day7
  • 解锁AI的“职业技能树“:Claude Skills深度技术解析——从原理到实战的完全指南