Java | Master timing tasks in one minute | 4 - Multi-threaded Timer

Author: Mars Sauce

Disclaimer: This article was originally created by Mars sauce, and part of the content comes from the Internet. If you have any questions, please contact me.

Reprint: Welcome to reprint, please contact me before reprinting!

foreword

The Timer that comes with the JDK cannot achieve multi-task concurrency, so how do we deal with the concurrency of multi-task timing? In this chapter, Mars sauce will be studied.

What Mars sauce can think of are keywords such as multi-threading and thread pool.

ScheduledExecutorService

ScheduledExecutorService is an interface class that inherits ExecutorService. ExecutorServiceMars sauce remembers in Java | One Minute to Master Asynchronous Programming | 3 - Thread Asynchronous - Nuggets (juejin.cn) mentioned that when using multi-threading to achieve asynchrony, creating a thread pool is to use Created by Executors, create a thread pool of task type here, we can use:

// mars酱
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool();
复制代码

Rewriting the previous example

When we used Timer before, we encountered blocking. This time we use the task thread pool to do it. Change the example in the previous article:

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @author mars酱
 */
public class MarsTimer {
    public static void main(String[] args) {
//        java.util.Timer timer = new Timer();
//        timer.schedule(new TimerTask() {
//            @Override
//            public void run() {
//                System.out.println("当前时间:" + new Date());
//            }
//        }, 1000, 5000);

        // 1. 创建第一个任务,打印时间后延迟5秒
        TimerTask tta = new TimerTask() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println(">> 这是a任务:当前时间:" + new Date());
                Thread.sleep(5000);
            }
        };

        // 2. 创建第二个任务,直接打印毫秒数
        TimerTask ttb = new TimerTask() {
            @Override
            public void run() {
                System.out.println("<< 这是b任务:当前毫秒:" + System.currentTimeMillis());
            }
        };

//        Timer timera = new Timer();
//        // 3. 把两个任务都加入计时器中
//        timera.schedule(tta, 1000, 5000);
//        timera.schedule(ttb, 1000, 5000);
        // 3. 创建一个核心线程为5的池
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
        // 4. 塞入第一个任务,5秒的一遍
        scheduledExecutorService.scheduleAtFixedRate(tta, 1000, 5000, TimeUnit.MILLISECONDS);
        // 5. 塞入第二个任务,也是5秒一遍
        scheduledExecutorService.scheduleAtFixedRate(ttb, 1000, 5000, TimeUnit.MILLISECONDS);
    }
}
复制代码

After rewriting, run it, and the result is:

Well, it’s very neat. Both tasks a and b are running independently, which perfectly solves the problem of blocking caused by shared queues.

here comes the problem

What if Mars-chan's task is performed once a month, or the birthday task is performed once a year?

Although the schedule function in ScheduledExecutorService supports periodic units up to days, if it is a weekly, monthly, or annual task, we have to calculate the time by ourselves when the next task is executed, which is still a bit flawed. Solve it There are also ways: just support cron expressions. But the method provided by ScheduledExecutorService does not support cron expressions. The following is the introduction of the cron expression

cron expression

The cron expression is a string with time meaning, the string starts with 5Separated by 6 spaces, divided into 67个域,格式为X X X X X X X。其中X是一个域的占位符。最后一个代表年份的域非必须,可省略。单个域有多个取值时,使用半角逗号 , 隔开取值。每个域可以是确定的取值,也可以是具有逻辑意义的特殊字符。每个域最多支持一个前导零。比如:

0 10 01 ? * * 2023
复制代码

表示2023年每天凌晨1点10分执行任务

域占位符的取值

下表为cron表达式中支持的特殊字符,以及含义:

特殊字符 含义 示例
***** 所有可能的值。 在月域中, ***** 表示每个月;在星期域中, ***** 表示星期的每一天。
, 列出枚举值。 在分钟域中,5,20表示分别在5分钟和20分钟触发一次。
- 范围。 在分钟域中,5-20表示从5分钟到20分钟之间每隔一分钟触发一次。
/ 指定数值的增量。 在分钟域中,0/15表示从第0分钟开始,每15分钟。在分钟域中3/20表示从第3分钟开始,每20分钟。
? 不指定值,仅日期和星期域支持该字符。 当日期或星期域其中之一被指定了值以后,为了避免冲突,需要将另一个域的值设为 ?
L 单词Last的首字母,表示最后一天,仅日期和星期域支持该字符。说明 指定L字符时,避免指定列表或者范围,否则,会导致逻辑问题。 - In a date field, L indicates the last day of a month. In the day of the week field, L represents the last day of the week, which is Sunday ( SUN ).
  • If there is specific content before L , for example, 6L in the week field means the last Saturday of this month. | | W | Valid weekdays except weekends, the event is triggered on the nearest valid weekday to the specified date. The W character will not cross the current month when searching for the nearest effective working day, and the character LW is used to indicate the last working day of the specified month. | In the date field 5W , if the 5th is a Saturday, it will be triggered on the nearest working day, Friday, that is, the 4th. If the 5th is Sunday, it will be triggered on the nearest working day, Monday, that is, the 6th; if the 5th falls on a day from Monday to Friday, it will be triggered on the 5th. | | # | Determine the day of the week of each month, only the week field supports this character. | In the week field, 4#2 means the second Thursday of a certain month.

Which scheduled tasks support cron expressions?

The Linux operating system supports cron expressions, and Java supports cron expressions with the Spring framework.

Get off at the station. See you at the next station.

Guess you like

Origin juejin.im/post/7229374487174889509