java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java ScheduledThreadPoolExecutor

一文带你学会Java中ScheduledThreadPoolExecutor使用

作者:JWASX

ScheduledThreadPoolExecutor是Java并发包中的一个类,同时也是 ThreadPoolExecutor的一个子类,本文主要为大家介绍一下ScheduledThreadPoolExecutor使用,需要的可以参考下

1. 概要

前面文章的地址:

定时/延时任务-自己实现一个简单的定时器

定时/延时任务-Timer用法

ScheduledThreadPoolExecutor 是 Java 并发包 (java.util.concurrent) 中的一个类,同时也是 ThreadPoolExecutor 的一个子类,这就意味者 ScheduledThreadPoolExecutor 不像 Timer 中使用单个线程去执行任务,ScheduledThreadPoolExecutor 使用了线程池去执行,同时 ScheduledThreadPoolExecutor 也具备了 Timer 中的各种功能。

2. 固定速率和固定延时

2.1 固定速率

固定速率 策略表示任务在固定的时间间隔内重复执行,不管任务的执行时间有多长,如果任务的执行时间超过了时间间隔,那么下一个任务会在当前任务执行完毕之后就会马上开始执行,下面是一个例子:假设我们设置了一个固定速率为 5 的任务,从 0s 开始执行,也就是说这个任务 5s 执行一次:

2.2 固定延时

固定延时 策略表示任务在当前任务执行完成之后,固定延时一段时间再执行下一个任务,下面是一个例子:假设我们设置了一个固定速率为 5 的任务,从 0s 开始执行,也就是说这个任务 5s 执行一次:

上面的例子中其实固定速率的执行和 Timer 是一样的,但是固定延时有点不一样,不知道你有没有发现,固定延时下执行的时候每一个任务和前一个任务的时间间隔一定是 任务执行时间 + 延时时间,其实相比于 Timer,ScheduledThreadPoolExecutor 的固定延时看起来更像是 “正宗” 一点的固定延时

3. API 解释

3.1 schedule

还是老规矩,先看下 ScheduledThreadPoolExecutor 的几个 API,看看用法是什么样的,首先就是 schedule 方法,这个方法就是普通的延时方法,只执行一次,非周期调度

public class Pra {

    public static void main(String[] args) {
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(16);
        Thread thread = new Thread(() -> {
            try {
                System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime());
                if (Math.random() < 0.5) {
                    System.out.println("sleep: 3s");
                    Thread.sleep(3000);
                } else {
                    System.out.println("sleep: 8s");
                    Thread.sleep(8000);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread-executor-run");
        executor.schedule(thread, 0, TimeUnit.SECONDS);
    }

    private static String getTime() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        return sdf.format(new Date());
    }

}

执行结果如下所示,延迟 0s 就开始执行任务

ScheduledThreadPoolExecutor 也提供了一个 callable 类型的任务的实现,使用 Callable 的好处就是可以获取任务的实现返回值,如下例子所示:

public class Pra {

    public static void main(String[] args) {
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(16);

        // 创建一个 Callable 任务
        Callable<Integer> task = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("Task is running on thread: " + Thread.currentThread().getName());
                return 1; // 返回结果
            }
        };

        // 调度 Callable 任务,在 0 秒后执行
        ScheduledFuture<Integer> future = executor.schedule(task, 0, TimeUnit.SECONDS);
        try {
            // 获取任务的结果
            Integer result = future.get();
            System.out.println("Task result: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    private static String getTime() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        return sdf.format(new Date());
    }

}

输出结果如下所示:

3.2 固定延时 - scheduleWithFixedDelay

public class Pra {

    public static void main(String[] args) {
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(16);

        Thread thread = new Thread(() -> {
            try {
                System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime());
                if (Math.random() < 0.5) {
                    System.out.println("sleep: 3s");
                    Thread.sleep(3000);
                } else {
                    System.out.println("sleep: 8s");
                    Thread.sleep(8000);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread-executor-run");

        executor.scheduleWithFixedDelay(thread, 0, 5, TimeUnit.SECONDS);
    }

    private static String getTime() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        return sdf.format(new Date());
    }

}

输入如下所示:

来分析一下:

首先第一次任务 2024-11-24 19:19:36:698 启动,然后执行时间 3s

第二次任务在 2024-11-24 19:19:44:718 启动,跟第一次任务相差 8s,延时 = 5s + 3s = 8s

第三次任务在 2024-11-24 19:19:52:741 启动,跟第二次任务相差 8s,延时 = 5s + 3s = 8s

第四次任务在 2024-11-24 19:20:05:760 启动,跟第三次任务相差 13s,延时 = 5s + 8s = 13s

第五次任务在 2024-11-24 19:20:13:773 启动,跟第四次任务相差 8s,延时 = 5s + 3s = 8s

第六次任务在 2024-11-24 19:20:26:787 启动,跟第五次任务相差 13s,延时 = 5s + 8s = 13s

可以看到 scheduleWithFixedDelay 的延时是相对于当前时间 + 延时时间的,跟 Timer 的固定延时任务有点不同

3.2 固定速率 - scheduleWithFixedDelay

public class Pra {

    public static void main(String[] args) {
        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(16);

        Thread thread = new Thread(() -> {
            try {
                System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime());
                if (Math.random() < 0.5) {
                    System.out.println("sleep: 3s");
                    Thread.sleep(3000);
                } else {
                    System.out.println("sleep: 8s");
                    Thread.sleep(8000);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread-executor-run");

        executor.scheduleAtFixedRate(thread, 0, 5, TimeUnit.SECONDS);
    }

    private static String getTime() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        return sdf.format(new Date());
    }

}

输出结果如下:

来分析下上面的执行流程,需要注意的是,ScheduledThreadPoolExecutor 的固定速率任务会在当前任务执行完之后再添加下一次要执行的任务

其实上面的解释中可以把后面的毫秒去掉,这样比较方便理解

4. 小结

上面就是几个 API 了,其中固定速率的和 Timer 的实现方式有点不同,后续文章中,我会对 ScheduledThreadPoolExecutor 的源码进行详细的解析,当然在解析 ScheduledThreadPoolExecutor 之前首先需要知道下线程池的工作原理和工作流程。

以上就是一文带你学会Java中ScheduledThreadPoolExecutor使用的详细内容,更多关于Java ScheduledThreadPoolExecutor的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文