• 面试突击34:如何使用线程池执行定时任务?


    在 Java 语言中,有两个线程池可以执行定时任务:ScheduledThreadPool 和 SingleThreadScheduledExecutor,其中 SingleThreadScheduledExecutor 可以看做是 ScheduledThreadPool 的单线程版本,它的用法和 ScheduledThreadPool 是一样的,所以本文重点来看 ScheduledThreadPool 线程池的使用。
    ScheduledThreadPool 执行定时任务的方法有以下 3 个:

    1. 使用 schedule 方法执行定时任务,只执行一次定时任务。
    2. 使用 scheduleAtFixedRate 方法执行定时任务,执行多次定时任务。
    3. 使用 scheduleWithFixedDelay 方法执行定时任务,执行多次定时任务。

    接下来我们看这 3 个方法的具体使用和区别。

    1.schedule

    schedule 方法只能执行一次定时任务,它需要传递 3 个参数:

    • 第 1 个参数:传递一个任务,Runnable 或 Callable 对象;
    • 第 2 个参数:添加定时任务后,再过多久开始执行定时任务;
    • 第 3 个参数:时间单位,配合参数 2 一起使用。

    下面我们创建一个 3 秒以后执行的定时任务:

    import java.time.LocalDateTime;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    public class ScheduledThreadPoolExample {
        public static void main(String[] args) throws InterruptedException {
            // 创建 ScheduledThreadPool 线程池
            ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(10);
            System.out.println("schedule 方法添加任务:" + LocalDateTime.now());
            threadPool.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println("执行 schedule 方法:" + LocalDateTime.now());
                }
            }, 3, TimeUnit.SECONDS); // 3s 之后执行
    
            // 以下代码是给业务方法一个时间对照信息
            TimeUnit.SECONDS.sleep(10); // 休眠 10s
            System.out.println("当前时间:" + LocalDateTime.now());
        }
    }
    

    以上程序的执行结果如下图所示:
    image.png
    从上述结果中可以看出,使用 schedule 方法只能执行一次定时任务。

    2.scheduleAtFixedRate

    scheduleAtFixedRate 方法可以执行多次定时任务,此方法需要 4 个参数:

    • 第 1 个参数:传递一个任务,Runnable 或 Callable 对象;
    • 第 2 个参数:添加定时任务后,再过多久开始执行定时任务;
    • 第 3 个参数:定时任务执行的时间间隔;
    • 第 4 个参数:时间单位,配合参数 2 和参数 3 一起使用。

    下面我们创建一个 3 秒后执行的定时任务,每个定时任务执行的时间间隔为 2 秒,实现代码如下:

    import java.time.LocalDateTime;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    public class ScheduledThreadPoolExample {
        public static void main(String[] args) throws InterruptedException {
            // 创建 ScheduledThreadPool 线程池
            ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(10);
            System.out.println("scheduleAtFixedRate 方法添加任务:" + LocalDateTime.now());
            threadPool.scheduleAtFixedRate(new Runnable() {
                                               @Override
                                               public void run() {
                                                   System.out.println("执行 scheduleAtFixedRate 方法:" + LocalDateTime.now());
                                                   // 休眠 2s
                                                   try {
                                                       TimeUnit.SECONDS.sleep(2);
                                                   } catch (InterruptedException e) {
                                                       e.printStackTrace();
                                                   }
                                               }
                                           },
                    3L, // 3s 后开始执行定时任务
                    2L, // 定时任务的执行间隔为 2s
                    TimeUnit.SECONDS); // 描述上面两个参数的时间单位
        }
    }
    

    以上程序的执行结果如下图所示:
    image.png
    从上述结果可以看出,当任务添加成功之后,3s 后开始执行第一个定时任务,之后每隔 2s 执行一次定时任务。

    3.scheduleWithFixedDelay

    scheduleWithFixedDelay 方法的使用和 scheduleAtFixedRate 类似,但执行效果完全不同,这个很容易理解如果效果一样就不用创建两个方法了。
    scheduleWithFixedDelay 方法是在方法执行完成之后,再隔 N 秒执行下一个定时任务,和 scheduleAtFixedRate 的固定时间执行不同,scheduleWithFixedDelay 方法的执行受定时任务执行的时长影响,比如以下代码:

    import java.time.LocalDateTime;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    public class ScheduledThreadPoolExample {
        public static void main(String[] args) throws InterruptedException {
            // 创建 ScheduledThreadPool 线程池
            ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(10);
            System.out.println("scheduleWithFixedDelay 方法添加任务:" + LocalDateTime.now());
            threadPool.scheduleWithFixedDelay(new Runnable() {
                                                  @Override
                                                  public void run() {
                                                      System.out.println("执行 scheduleWithFixedDelay 方法:" + LocalDateTime.now());
                                                      // 休眠 2s
                                                      try {
                                                          TimeUnit.SECONDS.sleep(2);
                                                      } catch (InterruptedException e) {
                                                          e.printStackTrace();
                                                      }
                                                  }
                                              },
                    3L, // 3s 后开始执行定时任务
                    2L, // 定时任务执行完 2s 之后,再执行下一个定时任务
                    TimeUnit.SECONDS); // 描述上面两个参数的时间单位
        }
    }
    

    以上程序的执行结果如下图所示:
    image.png
    从上述结果可以看出,定时任务在 3s 之后开始执行,以后每隔 4s 执行一次,这 4s 包含了,定时任务执行花费的 2s,加上每隔 2s 执行一次的时间间隔,也就是说 scheduleWithFixedDelay 是在任务执行完 N 秒之后,再执行下一次定时任务

    总结

    线程池执行定时任务的实现方法有 3 个:

    1. 使用 schedule 方法执行定时任务,只执行一次定时任务。
    2. 使用 scheduleAtFixedRate 方法执行定时任务,执行多次定时任务,它的执行时间间隔是固定的,不受定时任务执行时长影响(定时任务时间间隔 > 任务执行时间)。
    3. 使用 scheduleWithFixedDelay 方法执行定时任务,执行多次定时任务,它是在定时任务执行完之后,再隔 N 秒开始执行下一次定时任务,它的执行时间受定时任务执行时长影响。

    是非审之于己,毁誉听之于人,得失安之于数。

    公众号:Java面试真题解析

    面试合集:https://gitee.com/mydb/interview

  • 相关阅读:
    十六、JSONObject与JSONArray使用-不刷新页面做回写显示
    十二、js去掉空格_比较字符长度_中英文判断_页面初始化_简体字与繁字体判断
    十、页面加载时对复选框、下拉列表、单选框默认进行选中及提交请求时判断单选按钮是否选中
    一、什么是Velocity及简单示例
    十八、oracle查看、扩展表空间及linux服务器硬盘容量大小查看
    接口限流算法
    浅析负载均衡的6种算法,Ngnix的5种算法
    今天是JAVA诞生日
    浅谈Spring中JDK动态代理与CGLIB动态代理
    Docker基本命令汇总
  • 原文地址:https://www.cnblogs.com/vipstone/p/16066002.html
Copyright © 2020-2023  润新知