周期性线程池 newScheduledThreadPool

[Q&A] newScheduledThreadPool定义

ScheduledThreadPoolExecutor主要用来延迟运行任务,或者定期执行任务。
ScheduledThreadPoolExecutor的功能与Timer类似。Timer对应的是单个后台线程,而ScheduledThreadPoolExecutor可以对应多个后台线程数。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
    
public ScheduledThreadPoolExecutor(int corePoolSize) {
    ThreadPoolExecutor (corePoolSize, 
	    			    Integer.MAX_VALUE,     // DelayQueue是一个无界队列,所以设置maximumPoolSize的大小没有什么效果
	     				10, TimeUnit.MILLI_SCALE, 
	     				new DelayedWorkQueue());
}

[Q&A] ScheduledFutureTask主要包含3个成员变量

long型成员变量time,          表示这个任务将要被执行的具体时间。
long型成员变量sequenceNumber,表示这个任务被添加到ScheduledThreadPoolExecutor中的序号。
long型成员变量period,        表示任务执行的间隔周期。

[Q&A] DelayedWorkQueue排序规则

# 也就是说,执行时间短的在前面,如果两个任务的执行时间相同,那么先提交的任务将被先执行

ScheduledFutureTask的 time 小的排在前面(时间早的任务将被先执行)。
如果两个ScheduledFutureTask的 time 相同,sequenceNumber小的排在前面

[Q&A] ScheduledThreadPoolExecutor常用方法

schedule()                 延时 执行

scheduleWithFixedDelay()   以固定的 延时 执行
延时(delay):指的是一次执行终止和下一次执行开始之间的间隔。

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

[Q&A] ScheduledThreadPoolExecutor的执行

ScheduledThreadPoolExecutor的执行主要分为两大部分。
1)当调用ScheduledThreadPoolExecutor的scheduleAtFixedRate()方法或者scheduleWithFixedDelay()方法时,会向ScheduledThreadPoolExecutor的DelayQueue添加一个实现了RunnableScheduledFutur接口的ScheduledFutureTask
2)线程池中的线程从DelayQueue中获取ScheduledFutureTask,然后执行任务。

[Q&A] 把ScheduledFutureTask放入DelayQueue中的过程

# 添加任务分为3大步骤。
# 1)获取Lock。

# 2)添加任务。
·向PriorityQueue添加任务。
·如果在上面2.1中添加的任务是PriorityQueue的头元素,唤醒在Condition中等待的所有线程。

# 3)释放Lock。

在这里插入图片描述

[Q&A] 获取ScheduledFutureTask任务的过程

# 获取任务分为3大步骤
# 1)获取Lock。

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

# 3)释放Lock。

在这里插入图片描述


[Q&A] ScheduledThreadPoolExecutor中的线程1执行某个周期任务的4个步骤

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

在这里插入图片描述

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


常见用途

参考:Executor框架的成员


线程任务

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. 延迟10秒执行,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秒执行

2. 延迟1秒,间隔4秒再执行,scheduleWithFixedDelay(new Task(), 2, 4, TimeUnit.SECONDS)

public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,    // the task to execute
                                                 long initialDelay,   // the time to delay first execution
                                                 long delay,          // the delay between the termination of one execution and the commencement of the next
                                                 TimeUnit 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. 延时1秒, 周期4秒,scheduleAtFixedRate(new Task(), 1, 4, TimeUnit.SECONDS)

执行结果分析
前置条件任务用时 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 2023period(周期) 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 2023period(周期) 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 2023period(周期) 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 202320:59:57开始执行时间
pool-1-thread-1 → 定时任务 Start Time = Sat May 20 20:27:57 CST 202321:00:00当天9点开始执行
pool-1-thread-1 → 定时任务 End Time = Sat May 20 20:28:00 CST 202321:00:03任务执行完毕
pool-1-thread-1 → 定时任务 Start Time = Sat May 20 20:28:07 CST 202321:00:0029点重新执行
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 202321:00:03开始执行时间
pool-1-thread-1 → 定时任务 Start Time = Sat May 20 20:41:57 CST 202321:00:0029点开始执行
pool-1-thread-1 → 定时任务 End Time = Sat May 20 20:42:00 CST 202321:00:03任务执行完毕
pool-1-thread-1 → 定时任务 Start Time = Sat May 20 20:42:07 CST 202321:00:0039点重新执行
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
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值