周期性线程池 newScheduledThreadPool

这篇具有很好参考价值的文章主要介绍了周期性线程池 newScheduledThreadPool。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

newScheduledThreadPool

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
    
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
}
ScheduledThreadPoolExecutor继承自ThreadPoolExecutor。它主要用来在给定的延迟之后运行任务,或者定期执行任务。ScheduledThreadPoolExecutor的功能与Timer类似,但ScheduledThreadPoolExecutor功能更强大、更灵活。Timer对应的是单个后台线程,而ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数。
Creates a thread pool that can schedule commands to run after a given delay, or to execute periodically.
DelayQueue是一个无界队列,所以ThreadPoolExecutor的maximumPoolSize在Scheduled-ThreadPoolExecutor中没有什么意义(设置maximumPoolSize的大小没有什么效果)。
ScheduledThreadPoolExecutor的执行主要分为两大部分。
1)当调用ScheduledThreadPoolExecutor的scheduleAtFixedRate()方法或者scheduleWithFixedDelay()方法时,会向ScheduledThreadPoolExecutor的DelayQueue添加一个实现了RunnableScheduledFutur接口的ScheduledFutureTask
2)线程池中的线程从DelayQueue中获取ScheduledFutureTask,然后执行任务。

ScheduledThreadPoolExecutor的实现

ScheduledThreadPoolExecutor会把待调度的任务(ScheduledFutureTask)放到一个DelayQueue中。
ScheduledFutureTask 主要包含3个成员变量,如下。
·long型成员变量time,表示这个任务将要被执行的具体时间
·long型成员变量sequenceNumber,表示这个任务被添加到ScheduledThreadPoolExecutor中的序号
·long型成员变量period,表示任务执行的间隔周期
DelayQueue 封装了一个 PriorityQueue ,这个PriorityQueue会对队列中的Scheduled-FutureTask进行排序。排序时,time小的排在前面(时间早的任务将被先执行)。如果两个ScheduledFutureTask的time相同,就比较sequenceNumber,sequenceNumber小的排在前面(也就是说,如果两个任务的执行时间相同,那么先提交的任务将被先执行)
下图是ScheduledThreadPoolExecutor中的线程1执行某个周期任务的4个步骤。

周期性线程池 newScheduledThreadPool

1)线程1从DelayQueue中获取已到期的ScheduledFutureTask(DelayQueue.take())。到期任务是指ScheduledFutureTask的time大于等于当前时间。
2)线程1执行这个ScheduledFutureTask。
3)线程1修改ScheduledFutureTask的time变量为下次将要被执行的时间。
4)线程1把这个修改time之后的ScheduledFutureTask放回DelayQueue中(Delay-Queue.add())。

获取任务的过程
周期性线程池 newScheduledThreadPool

获取任务分为3大步骤
1)获取Lock。
2)获取周期任务。
·如果PriorityQueue为空,当前线程到Condition中等待;否则执行下面的2.2。
·如果PriorityQueue的头元素的time时间比当前时间大,到Condition中等待到time时间;否则执行下面的2.3。
·获取PriorityQueue的头元素(2.3.1);如果PriorityQueue不为空,则唤醒在Condition中等待的所有线程(2.3.2)。
3)释放Lock。
ScheduledThreadPoolExecutor在一个循环中执行步骤2,直到线程从PriorityQueue获取到一个元素之后(执行2.3.1之后),才会退出无限循环(结束步骤2)。

把ScheduledFutureTask放入DelayQueue中的过程

周期性线程池 newScheduledThreadPool

添加任务分为3大步骤。
1)获取Lock。
2)添加任务。
·向PriorityQueue添加任务。
·如果在上面2.1中添加的任务是PriorityQueue的头元素,唤醒在Condition中等待的所有线程。
3)释放Lock。

-----------------------------------------------------------------------------读书笔记摘自 书名:Java并发编程的艺术 作者:方腾飞;魏鹏;程晓明

常见用途

参考:Executor框架的成员文章来源地址https://www.toymoban.com/news/detail-453505.html

线程任务

public class Task implements Runnable {
    private final String name;

    public Task(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " → " + name + " Start Time = " + new Date());
        processCommand();
        System.out.println(Thread.currentThread().getName() + " → " + name + " End   Time = " + new Date());
    }

    private void processCommand() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

1. schedule(new Task(), 10, TimeUnit.SECONDS)

public class ScheduledThreadPool {
    public static void main(String args[]) throws InterruptedException, ExecutionException {

        System.out.println(Thread.currentThread().getName() + "线程: Start at: " + new Date());

        ScheduledThreadPoolExecutor exec = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(5);

        for (int i = 0; i < 10; i++) {
            System.out.println("添加了第" + i + "个线程任务 " + new Date());
            exec.schedule(new Task("线程名字" + i), 10, TimeUnit.SECONDS);// 延迟10秒执行
        }

        exec.shutdown();

        System.out.println(Thread.currentThread().getName() + "线程: 打卡" + new Date());

        while (!exec.isTerminated()) {
            // wait for all tasks to finish
        }

        System.out.println(Thread.currentThread().getName() + "线程: Finished all threads at:" + new Date());
    }
}

执行结果分析

执行结果
main线程: Start at: Sat May 20 12:51:32 CST 2023
添加了第0个线程任务 Sat May 20 12:51:32 CST 2023
添加了第1个线程任务 Sat May 20 12:51:32 CST 2023
添加了第2个线程任务 Sat May 20 12:51:32 CST 2023
添加了第3个线程任务 Sat May 20 12:51:32 CST 2023
添加了第4个线程任务 Sat May 20 12:51:32 CST 2023
添加了第5个线程任务 Sat May 20 12:51:32 CST 2023
添加了第6个线程任务 Sat May 20 12:51:32 CST 2023
添加了第7个线程任务 Sat May 20 12:51:32 CST 2023
添加了第8个线程任务 Sat May 20 12:51:32 CST 2023
添加了第9个线程任务 Sat May 20 12:51:32 CST 2023
main线程: 打卡Sat May 20 12:51:32 CST 2023
pool-1-thread-5 → 线程名字2 Start Time = Sat May 20 12:51:42 CST 2023
pool-1-thread-3 → 线程名字1 Start Time = Sat May 20 12:51:42 CST 2023
pool-1-thread-4 → 线程名字3 Start Time = Sat May 20 12:51:42 CST 2023
pool-1-thread-2 → 线程名字4 Start Time = Sat May 20 12:51:42 CST 2023
pool-1-thread-1 → 线程名字0 Start Time = Sat May 20 12:51:42 CST 2023
pool-1-thread-3 → 线程名字1 End Time = Sat May 20 12:51:45 CST 2023
pool-1-thread-5 → 线程名字2 End Time = Sat May 20 12:51:45 CST 2023
pool-1-thread-3 → 线程名字5 Start Time = Sat May 20 12:51:45 CST 2023
pool-1-thread-5 → 线程名字6 Start Time = Sat May 20 12:51:45 CST 2023
pool-1-thread-4 → 线程名字3 End Time = Sat May 20 12:51:45 CST 2023
pool-1-thread-4 → 线程名字7 Start Time = Sat May 20 12:51:45 CST 2023
pool-1-thread-2 → 线程名字4 End Time = Sat May 20 12:51:45 CST 2023
pool-1-thread-2 → 线程名字8 Start Time = Sat May 20 12:51:45 CST 2023
pool-1-thread-1 → 线程名字0 End Time = Sat May 20 12:51:45 CST 2023
pool-1-thread-1 → 线程名字9 Start Time = Sat May 20 12:51:45 CST 2023
pool-1-thread-3 → 线程名字5 End Time = Sat May 20 12:51:48 CST 2023
pool-1-thread-5 → 线程名字6 End Time = Sat May 20 12:51:48 CST 2023
pool-1-thread-4 → 线程名字7 End Time = Sat May 20 12:51:48 CST 2023
pool-1-thread-2 → 线程名字8 End Time = Sat May 20 12:51:48 CST 2023
pool-1-thread-1 → 线程名字9 End Time = Sat May 20 12:51:48 CST 2023
main线程: Finished all threads at:Sat May 20 12:51:48 CST 2023
从控制台结果可以看出:
5个线程去执行10个线程任务,线程任务放到线程池后延迟10秒执行

scheduleAtFixedRate() scheduleWithFixedDelay()
以固定的 频率 执行 以固定的 延时 执行
period(周期)指的是两次成功执行之间的时间。
上一个任务开始的时间计时,一个period后,检测上一个任务是否执行完毕,
如果上一个任务执行完毕,则当前任务立即执行,
如果上一个任务没有执行完毕,则需要等上一个任务执行完毕后立即执行。
delay(延时)指的是一次执行终止和下一次执行开始之间的延迟。

2. scheduleWithFixedDelay(new Task(), 2, 4, TimeUnit.SECONDS) 初始延时 2s, 固定延时4s

public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                 long initialDelay,
                                                 long delay,
                                                 TimeUnit unit);
参数 含义
command the task to execute
initialDelay the time to delay first execution
delay the delay between the termination of one execution and the commencement of the next
unit the time unit of the initialDelay and delay parameters
public class FixedDelay {
    public static void main(String args[]) throws InterruptedException, ExecutionException {
        way1();
        way2();
    }

    private static void way2() {
        System.out.println(Thread.currentThread().getName() + "线程: Start at: " + new Date());

        ScheduledThreadPoolExecutor scheduledExecutorService = new ScheduledThreadPoolExecutor(1);

        scheduledExecutorService.scheduleWithFixedDelay(() -> {
            System.out.println(Thread.currentThread().getName() + " → " + " Start Time = " + new Date());
            try {
                sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName() + " → " + " End   Time = " + new Date());
        }, 1, 4, TimeUnit.SECONDS);  // 延迟 1s,周期 4s
    }

    private static void way1() {
        System.out.println(Thread.currentThread().getName() + "线程: Start at: " + new Date());

        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);

        scheduledExecutorService.scheduleWithFixedDelay(new Task("任务"), 1, 4, TimeUnit.SECONDS);  // 延迟 1s, 周期 4s
    }
}

执行结果分析

执行结果 分析
main线程: Start at: Sat May 20 18:54:29 CST 2023
pool-1-thread-1 → 任务 Start Time = Sat May 20 18:54:30 CST 2023 延迟 1 s
pool-1-thread-1 → 任务 End Time = Sat May 20 18:54:33 CST 2023
pool-1-thread-1 → 任务 Start Time = Sat May 20 18:54:37 CST 2023 等待 4 s 再开始
pool-1-thread-1 → 任务 End Time = Sat May 20 18:54:40 CST 2023
pool-1-thread-1 → 任务 Start Time = Sat May 20 18:54:44 CST 2023 等待 4 s 再开始
pool-1-thread-1 → 任务 End Time = Sat May 20 18:54:47 CST 2023
pool-1-thread-1 → 任务 Start Time = Sat May 20 18:54:51 CST 2023 等待 4 s 再开始
pool-1-thread-1 → 任务 End Time = Sat May 20 18:54:54 CST 2023

3. scheduleAtFixedRate(new Task(), 1, 4, TimeUnit.SECONDS) 初始延时 1s, 周期4s

执行结果分析

执行结果 分析
前置条件 任务用时 3 秒, period = 4
main线程: Starting at: Sat May 20 19:31:13 CST 2023
pool-1-thread-1 → 任务 Start Time = Sat May 20 19:31:14 CST 2023 延时 1
pool-1-thread-1 → 任务 End Time = Sat May 20 19:31:17 CST 2023 任务用时 3
pool-1-thread-1 → 任务 Start Time = Sat May 20 19:31:18 CST 2023 period(周期) 4
pool-1-thread-1 → 任务 End Time = Sat May 20 19:31:21 CST 2023
pool-1-thread-1 → 任务 Start Time = Sat May 20 19:31:22 CST 2023
pool-1-thread-1 → 任务 End Time = Sat May 20 19:31:25 CST 2023
任务用时 < period(周期) ,等待时长为:period 需要等待period再开始下一个
前置条件 任务用时 3 秒, period = 3
main线程: Starting at: Sat May 20 19:35:33 CST 2023
pool-1-thread-1 → 任务 Start Time = Sat May 20 19:35:34 CST 2023
pool-1-thread-1 → 任务 End Time = Sat May 20 19:35:37 CST 2023
pool-1-thread-1 → 任务 Start Time = Sat May 20 19:35:37 CST 2023 period(周期) 3
pool-1-thread-1 → 任务 End Time = Sat May 20 19:35:40 CST 2023
pool-1-thread-1 → 任务 Start Time = Sat May 20 19:35:40 CST 2023
pool-1-thread-1 → 任务 End Time = Sat May 20 19:35:43 CST 2023
pool-1-thread-1 → 任务 Start Time = Sat May 20 19:35:43 CST 2023
任务用时 = period(周期) ,等待时长为:period 需要等待period再开始下一个
前置条件 任务用时 3 秒, period = 2
main线程: Starting at: Sat May 20 19:37:03 CST 2023
pool-1-thread-1 → 任务 Start Time = Sat May 20 19:37:04 CST 2023
pool-1-thread-1 → 任务 End Time = Sat May 20 19:37:07 CST 2023
pool-1-thread-1 → 任务 Start Time = Sat May 20 19:37:07 CST 2023 period(周期) 2 秒 无效,以任务用时为准,故结束就开始
pool-1-thread-1 → 任务 End Time = Sat May 20 19:37:10 CST 2023
任务用时 > period(周期) 4 秒 ,等待时长为:任务用时 等任务结束后直接开始执行下一个

定时任务☞每天晚上9点执行一次

public class FixedRateDemo {
    public static void main(String args[]) throws InterruptedException, ExecutionException, ParseException {
        System.out.println(Thread.currentThread().getName() + "线程: Starting at: " + new Date());

        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        // long oneDay = 24 * 60 * 60 * 1000=86400000; // 1天24小时作为周期
        // long initDelay = getTimeMillis("21:00:00") - System.currentTimeMillis(); // 当前时间

        long oneDayPeriod = 10000; // 正常1天=24*60*60*1000=86400000ms,这里用5s来模拟
//        long initDelay = getTimeMillis("21:00:00") - getTimeMillis("20:59:57"); // 假设模拟此时当天的20:59:57 → 3000ms
        long initDelay = getTimeMillis("21:00:00") - getTimeMillis("21:00:03"); // 假设模拟此时当天的21:00:03 →-3000ms

        initDelay = initDelay > 0 ? initDelay : oneDayPeriod + initDelay;

        executor.scheduleAtFixedRate(new Task("定时任务"), initDelay, oneDayPeriod, TimeUnit.MILLISECONDS);
    }


    /**
     * 获取指定时间对应的毫秒数
     */
    private static long getTimeMillis(String time) throws ParseException {
        DateFormat dateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
        DateFormat dayFormat = new SimpleDateFormat("yy-MM-dd");
        Date curDate = dateFormat.parse(dayFormat.format(new Date()) + " " + time);
        return curDate.getTime();
    }
}

执行结果分析

执行结果 模拟数据 分析
前置条件:模拟20:59:57开始执行定时任务,21:00:00正式执行,周期10
main线程: Starting at: Sat May 20 20:27:54 CST 2023 20:59:57 开始执行时间
pool-1-thread-1 → 定时任务 Start Time = Sat May 20 20:27:57 CST 2023 21:00:00 当天9点开始执行
pool-1-thread-1 → 定时任务 End Time = Sat May 20 20:28:00 CST 2023 21:00:03 任务执行完毕
pool-1-thread-1 → 定时任务 Start Time = Sat May 20 20:28:07 CST 2023 21:00:00 29点重新执行
pool-1-thread-1 → 定时任务 End Time = Sat May 20 20:28:10 CST 2023
pool-1-thread-1 → 定时任务 Start Time = Sat May 20 20:28:17 CST 2023
pool-1-thread-1 → 定时任务 End Time = Sat May 20 20:28:20 CST 2023
pool-1-thread-1 → 定时任务 Start Time = Sat May 20 20:28:27 CST 2023
pool-1-thread-1 → 定时任务 End Time = Sat May 20 20:28:30 CST 2023
pool-1-thread-1 → 定时任务 Start Time = Sat May 20 20:28:37 CST 2023
前置条件:模拟21:00:03开始执行定时任务,21:00:00正式执行,周期10
main线程: Starting at: Sat May 20 20:41:50 CST 2023 21:00:03 开始执行时间
pool-1-thread-1 → 定时任务 Start Time = Sat May 20 20:41:57 CST 2023 21:00:00 29点开始执行
pool-1-thread-1 → 定时任务 End Time = Sat May 20 20:42:00 CST 2023 21:00:03 任务执行完毕
pool-1-thread-1 → 定时任务 Start Time = Sat May 20 20:42:07 CST 2023 21:00:00 39点重新执行
pool-1-thread-1 → 定时任务 End Time = Sat May 20 20:42:10 CST 2023
pool-1-thread-1 → 定时任务 Start Time = Sat May 20 20:42:17 CST 2023
pool-1-thread-1 → 定时任务 End Time = Sat May 20 20:42:20 CST 2023
pool-1-thread-1 → 定时任务 Start Time = Sat May 20 20:42:27 CST 2023
pool-1-thread-1 → 定时任务 End Time = Sat May 20 20:42:30 CST 2023

到了这里,关于周期性线程池 newScheduledThreadPool的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • flink生成水位线记录方式--周期性水位线生成器

    在flink基于事件的时间处理中,水位线记录的生成是一个很重要的环节,本文就来记录下几种水位线记录的生成方式的其中一种:周期性水位线生成器 1.1 BoundedOutOfOrdernessTimeStampExtractor 他会接收一个表示最大延迟的参数,比如1分钟,意味着如果到达的元素的事件时间和之前到

    2024年02月07日
    浏览(58)
  • Abaqus CAE 2018插件使用详解:基于周期性边界条件定义3D几何模型的实践指南**

    注:这篇文章是为了帮助读者更好地理解和使用Abaqus CAE 2018的插件来定义周期性边界条件。所提供的信息是基于我个人的理解和实践,如有不准确或者有更好的建议,欢迎读者们指正和交流。 一、 引言 在进行无限或半无限域建模时,周期性边界条件可为我们提供了一种模拟

    2024年02月11日
    浏览(48)
  • 使用Dream3D和MATLAB从综合构建微结构到创建具有周期性边界条件的Abaqus输入文件的一站式解决方案

    声明 :本文中的所有内容仅供学术研究和讨论,不保证完全无误。对于使用本文内容可能产生的任何后果,作者不承担任何责任。希望大家在使用时,结合自己的实际情况进行酌情调整。 当我们面临材料力学问题,包括材料的疲劳、断裂和塑性等行为的仿真时,一个常见的

    2024年02月10日
    浏览(77)
  • 为什么RIP使用UDP,OSPF使用IP,而BGP使用TCP?为什么RIP周期性地和邻站交换路由信息而BGP却不这样做?

    RIP只和邻站交换信息,使用UDP无可靠保障,但开销小,可以满足RIP要求; OSPF使用可靠的洪泛法,直接使用IP,灵活、开销小; BGP需要交换整个路由表和更新信息,TCP提供可靠交付以减少带宽消耗; RIP使用不保证可靠交付的UDP,因此必须不断地(周期性地)和邻站交换信息才

    2024年02月02日
    浏览(53)
  • 【正点原子STM32】RTC实时时钟(RTC方案、BCD码、时间戳、RTC相关寄存器和HAL库驱动、RTC基本配置步骤、RTC基本驱动步骤、时间设置和读取、RTC闹钟配置和RTC周期性自动唤醒配置)

    一、RTC简介 二、STM32 RTC框图介绍 2.1、STM32 F1 RTC结构框图 2.2、STM32 F4 / F7 / H7 RTC结构框图 三、RTC相关寄存器介绍 3.1、RTC基本配置步骤 3.2、RTC相关寄存器(F1) 3.3、RTC相关寄存器(F4 / F7 / H7) 四、RTC相关HAL库驱动介绍 4.1、RTC相关HAL库驱动(F1) 4.2、RTC相关HAL库驱动(F4 / F7 /

    2024年03月27日
    浏览(67)
  • 线程生命周期、线程通讯

            有关线程生命周期就要看下面这张图,围绕这张图讲解它的方法的含义,和不同方法间的区别。    1、yield()方法     yield()让当前正在运行的线程回到就绪,以允许具有相同优先级的其他线程获得运行的机会。但是,实际中无法保证yield()达到让步的目的,因为,让

    2024年02月09日
    浏览(34)
  • [Java]线程生命周期与线程通信

    【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) https://www.cnblogs.com/cnb-yuchen/p/18162522 出自【进步*于辰的博客】 线程生命周期与进程有诸多相似,所以我们很容易将两者关联理解并混淆,一些细节之处确有许多不同,因为线程调度与进程调度虽都由

    2024年04月27日
    浏览(35)
  • 进程与线程、线程创建、线程周期、多线程安全和线程池(ThreadPoolExecutor)

    什么进程? 进程是 资源(CPU、内存等)分配 的基本单位,它是程序执行时的一个实例。程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。 什么是线程? 线程是操作系

    2024年02月14日
    浏览(39)
  • Java线程生命周期详解

    Java中的线程生命周期是多线程开发的核心概念。了解线程的生命周期以及它们如何进行状态转换对于编写有效且无错误的多线程程序至关重要。 Java线程主要有以下几个状态,这些状态定义在Thread.State枚举类中: 新建状态(New) :当我们创建一个新的线程实例时,线程就处

    2024年02月11日
    浏览(38)
  • 高级进阶多线程——多任务处理、线程状态(生命周期)、三种创建多线程的方式

    Java中的多线程是一个同时执行多个线程的进程。线程是一个轻量级的子进程,是最小的处理单元。多进程和多线程都用于实现多任务处理。 但是,一般使用多线程而不是多进程,这是因为线程使用共享内存区域。它们不分配单独的内存区域以节省内存,并且线程之间的上下

    2024年02月13日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包