ScheduledExecutor使用之——重复创建停止周期性任务

一、背景

在Java中,ScheduledExecutorService是一个用于处理需要定时或周期性执行的任务的强大工具。如果你想要重复地创建和停止周期性任务,你可以使用ScheduledExecutorServicescheduleAtFixedRatescheduleWithFixedDelay方法,并且结合取消任务的能力来实现。

以下是一个简单的例子,展示了如何重复创建和停止周期性任务:

复制代码
import java.util.concurrent.*;

public class ScheduledTaskExample {

    private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    private ScheduledFuture<?> future; // 用于保存周期性任务的句柄

    // 启动周期性任务
    public void startPeriodicTask(Runnable task, long initialDelay, long period, TimeUnit unit) {
        // 如果之前已经有任务在运行,先取消它
        cancelPeriodicTask();

        // 提交新的周期性任务
        future = scheduler.scheduleAtFixedRate(task, initialDelay, period, unit);
    }

    // 停止周期性任务
    public void stopPeriodicTask() {
        // 取消任务
        if (future != null && !future.isCancelled()) {
            future.cancel(true); // 传入true表示如果任务正在执行则中断它
            future = null; // 清空future引用
        }
    }

    // 取消周期性任务(如果需要的话,你可以将这个方法合并到stopPeriodicTask中)
    private void cancelPeriodicTask() {
        if (future != null && !future.isCancelled()) {
            future.cancel(false); // 如果任务正在执行,则不中断它,只是停止后续的执行
        }
    }

    public static void main(String[] args) {
        ScheduledTaskExample example = new ScheduledTaskExample();

        // 示例任务
        Runnable task = () -> {
            System.out.println("周期性任务执行中..." + System.currentTimeMillis());
        };

        // 启动周期性任务,初始延迟1秒,之后每隔2秒执行一次
        example.startPeriodicTask(task, 1, 2, TimeUnit.SECONDS);

        // 等待一段时间来观察任务执行
        try {
            Thread.sleep(10000); // 等待10秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 停止周期性任务
        example.stopPeriodicTask();

        // 你可以再次启动任务...
        // example.startPeriodicTask(task, 1, 2, TimeUnit.SECONDS);

        // 最后,关闭调度器(如果你确定不再需要它)
        example.shutdownScheduler();
    }

    // 关闭调度器
    public void shutdownScheduler() {
        if (scheduler != null && !scheduler.isShutdown()) {
            scheduler.shutdown(); // 这不会立即停止正在执行的任务
            try {
                if (!scheduler.awaitTermination(60, TimeUnit.SECONDS)) {
                    scheduler.shutdownNow(); // 强制停止所有正在执行的任务
                }
            } catch (InterruptedException ie) {
                scheduler.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
    }
}

在这个例子中,startPeriodicTask方法用于启动周期性任务,它首先取消任何正在运行的任务(如果有的话),然后提交一个新的任务。stopPeriodicTask方法用于停止当前周期性任务。注意,future.cancel(true)会尝试中断正在执行的任务(如果任务支持中断的话),而future.cancel(false)则只会停止后续的执行。

最后,shutdownScheduler方法用于关闭ScheduledExecutorService。这在实际应用中是很重要的,因为如果你不再需要调度器,就应该关闭它以释放资源。请注意,关闭调度器并不会立即停止正在执行的任务,它会等待所有任务完成(或者你可以调用shutdownNow来强制停止它们)。

二、案例

复制代码
 private ScheduledExecutorService executor;
    private ScheduledFuture<?> future;

    private void excDurations() {

        // 创建线程池
//        executor = Executors.newScheduledThreadPool(1);
//        executor = Executors.newSingleThreadScheduledExecutor();
//        future = executor.scheduleAtFixedRate(runnable, 0, 30, TimeUnit.SECONDS);

        // 如果前一个任务还在运行,先取消它
        if (future != null && !future.isCancelled()) {
            future.cancel(true); // 如果任务正在执行,则可能无法立即停止
        }

        if (executor != null) {
            // 关闭executor服务
            executor.shutdownNow();
            try {
                // 等待服务关闭
                if (!executor.awaitTermination(200, TimeUnit.MILLISECONDS)) {
                    System.out.println("ExecutorService did not terminate in the given time.");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                Thread.currentThread().interrupt(); // 重新中断线程
            }
        }

        executor = null;

//        executor = Executors.newScheduledThreadPool(1);
        executor = Executors.newSingleThreadScheduledExecutor();
        future = executor.scheduleAtFixedRate(runnable, 0, 8, TimeUnit.HOURS);

    }
相关推荐
云烟成雨TD5 小时前
Spring AI Alibaba 1.x 系列【69】Token 用量统计
java·人工智能·spring
JAVA9655 小时前
JAVA面试-并发篇 03-使用synchronized doublecheck实现单例有什么坑
java·单例模式·面试
在繁华处5 小时前
Java从零到熟练(四):面向对象基础
java·开发语言
wuxia21186 小时前
微信小程序单击元素切换元素的显示和隐藏
javascript·微信小程序·setdata
JustHappy6 小时前
古法编程秘籍(二):什么是代码模块化?别背概念,把房间收拾明白就够了
前端·后端
小江的记录本6 小时前
【JVM虚拟机】堆内存分代模型:年轻代(Eden+Survivor)、老年代、元空间Metaspace(附《思维导图》+《面试高频考点清单》)
java·前端·jvm·后端·python·spring·面试
在繁华处6 小时前
Java从零到熟练(三):流程控制
java·开发语言·python
weixin_471383037 小时前
图片预解码缓存
前端·浏览器缓存·图片预解码
唐青枫7 小时前
Java Optional 实战指南:优雅处理空值与链式转换
java
一起学开源7 小时前
一文读懂 ReAct 范式:让 AI Agent 真正学会“思考+行动“
java·javascript·react.js·ecmascript·react·alibaba·智能体开发