Goodbye Spring Task

Timer


A timed task implementation that has been supported since JDK 1.3. Internally, the TaskQueue class is used to store timed tasks, which is relatively simple to use, but has many defects. For example, a Timer will start a thread, and the performance will be very poor if there are too many tasks. For example, if a TimerTask takes time during the execution of the task For a long time, it will affect the scheduling of other tasks.
@Slf4j
public class TimerDemo {
public static void main(String[] args) {
TimerTask task = new TimerTask() {
@Override
public void run() {
log.debug("current time{}thread name{}", DateTime.now(),
Thread.currentThread().getName());
}
};
log.debug("current time{}thread name{}", DateTime.now(),
Thread.currentThread().getName());
Timer timer = new Timer("TimerDemo");
timer.schedule(task, 1000L);
}
}

The log after the code runs is as follows:
13:11:45.268 [main] DEBUG top.springtask.TimerDemo - current time 2022-04-27 13:11:45 thread name main
13:11:46.280 [TimerDemo] DEBUG top.springtask.TimerDemo - current time 2022-04-27 13:11:46 thread name TimerDemo

ScheduledThreadPoolExecutor
The scheduled task provided by JDK 1.5 inherits ThreadPoolExecutor and implements the ScheduledExecutorService interface, so it supports task execution in concurrent scenarios. At the same time, the defects of Timer are optimized. However, since the queue is used to implement the timer, there are operations such as entering and leaving the queue, adjusting the heap, etc., so the timing is not very accurate (nitpicking).
@Slf4j
public class ScheduledThreadPoolExecutorDemo {
public static void main(String[] args) throws InterruptedException {
TimerTask task = new TimerTask() {
@Override
public void run() {
log.debug("current time{}thread name{}", DateTime.now(),
Thread.currentThread().getName());
}
};

log.debug("current time{}thread name{}", DateTime.now(),
Thread.currentThread().getName());
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
executorService.scheduleAtFixedRate(task, 1000L,1000L, TimeUnit.MILLISECONDS);
Thread.sleep(1000+1000*4);
executorService.shutdown();
}
}

The output is as follows:
14:43:41.740 [main] DEBUG top.springtask.ScheduledThreadPoolExecutorDemo - current time 2022-04-27 14:43:41 thread name main
14:43:42.752 [pool-1-thread-1] DEBUG top.springtask.ScheduledThreadPoolExecutorDemo - current time 2022-04-27 14:43:42 thread name pool-1-thread-1
14:43:43.748 [pool-1-thread-1] DEBUG top.springtask.ScheduledThreadPoolExecutorDemo - current time 2022-04-27 14:43:43 thread name pool-1-thread-1
14:43:44.749 [pool-1-thread-2] DEBUG top.springtask.ScheduledThreadPoolExecutorDemo - current time 2022-04-27 14:43:44 thread name pool-1-thread-2
14:43:45.749 [pool-1-thread-2] DEBUG top.springtask.ScheduledThreadPoolExecutorDemo - current time 2022-04-27 14:43:45 thread name pool-1-thread-2
14:43:46.749 [pool-1-thread-2] DEBUG top.springtask.ScheduledThreadPoolExecutorDemo - current time 2022-04-27 14:43:46 thread name pool-1-thread-2

Spring Task
Spring Task is a lightweight timing task tool provided by Spring, which means that there is no need to add third-party dependencies, which is more convenient and easy to use than other third-party class libraries.
It seems that there is no other nonsense to say about Spring Task, let's get started.
The first step is to create a new configuration class SpringTaskConfig and add the @EnableScheduling annotation to enable Spring Task.
@Configuration
@EnableScheduling
public class SpringTaskConfig {
}

Of course, you can also add the @EnableScheduling annotation directly to the main class without creating a new configuration class.
@SpringBootApplication
@EnableScheduling
public class CodingmoreSpringtaskApplication {

public static void main(String[] args) {
SpringApplication.run(CodingmoreSpringtaskApplication.class, args);
}

}

The second step is to create a new scheduled task class CronTask, and use the @Scheduled annotation to register Cron expressions to execute scheduled tasks.
@Slf4j
@Component
public class CronTask {
@Scheduled(cron = "0/1 * * ? * ?")
public void cron() {
log.info("Timed execution, time{}", DateUtil.now());
}
}

Start the server and find that the log will be printed every second, proving that the cron expression form of Spring Task has taken effect.

By default, the size of the thread pool created by @Scheduled is 1. If you want to increase the size of the thread pool, you can make the SpringTaskConfig class implement the SchedulingConfigurer interface and increase the size of the thread pool through setPoolSize.
@Configuration
@EnableScheduling
public class SpringTaskConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();

threadPoolTaskScheduler.setPoolSize(10);
threadPoolTaskScheduler.setThreadNamePrefix("my-scheduled-task-pool-");
threadPoolTaskScheduler.initialize();

taskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
}

After the service hot deployment is complete, you will see the following information on the console:

It can be confirmed that the custom thread pool size has taken effect. Some tasks use the thread led-task-pool-3, and some use the thread led-task-pool-7. After running for a long time, you can find led-task-pool-1 To led-task-pool-10 have.

In addition to supporting Cron expressions, Spring Task also has three usages: fixedRate (fixed rate execution), fixedDelay (fixed delay execution), and initialDelay (initial delay).
/**
* fixedRate: Fixed rate execution. Execute every 5 seconds.
*/
@Scheduled(fixedRate = 5000)
public void reportCurrentTimeWithFixedRate() {
log.info("Current Thread : {}", Thread.currentThread().getName());
log.info("Fixed Rate Task : The time is now {}", DateUtil.now());
}

/**
* fixedDelay: Fixed delay execution. Executed only 2 seconds after the last successful call.
*/
@Scheduled(fixedDelay = 2000)
public void reportCurrentTimeWithFixedDelay() {
try {
TimeUnit.SECONDS.sleep(3);
log.info("Fixed Delay Task : The time is now {}",DateUtil.now());
} catch (InterruptedException e) {
e.printStackTrace();
}
}

/**
* initialDelay: The initial delay. The first execution of the task will be delayed by 5 seconds, then it will be executed at regular intervals of 5 seconds.
*/
@Scheduled(initialDelay = 5000, fixedRate = 5000)
public void reportCurrentTimeWithInitialDelay() {
log.info("Fixed Rate Task with Initial Delay : The time is now {}", DateUtil.now());
}

but,There is a pit in fixedRate. If the fixed rate set by the timer of a certain method is executed every 5 seconds, this method now needs to perform the following four tasks. The time-consuming of the four tasks is: 6s, 6s, 2s, 3s, How will the task be executed (in a single-threaded environment)?
2022-04-27 15:25:52.400 INFO 4343 --- [led-task-pool-1] c.codingmore.component.PublishPostTask : Fixed Rate Task : The time is now 2022-04-27 15:25:52
2022-04-27 15:25:58.401 INFO 4343 --- [led-task-pool-1] c.codingmore.component.PublishPostTask : Fixed Rate Task : The time is now 2022-04-27 15:25:58
2022-04-27 15:26:00.407 INFO 4343 --- [led-task-pool-1] c.codingmore.component.PublishPostTask : Fixed Rate Task : The time is now 2022-04-27 15:26:00
2022-04-27 15:26:04.318 INFO 4343 --- [led-task-pool-1] c.codingmore.component.PublishPostTask : Fixed Rate Task : The time is now 2022-04-27 15:26:04

The relative start time of the first task is the 0th second, but since it was executed for 6 seconds, it should have been the task executed in the 5th second, which was delayed until the 6th second. The third task was delayed by 12 seconds. It should be executed in the 10th second, the third task has no delay and is executed normally after 15 seconds.
What if we use the @EnableAsync annotation to enable a multi-threaded environment?
2022-04-27 15:33:01.385 INFO 4421 --- [led-task-pool-1] c.codingmore.component.PublishPostTask : Fixed Rate Task : The time is now 2022-04-27 15:33:01
2022-04-27 15:33:07.390 INFO 4421 --- [led-task-pool-1] c.codingmore.component.PublishPostTask : Fixed Rate Task : The time is now 2022-04-27 15:33:07
2022-04-27 15:33:09.391 INFO 4421 --- [led-task-pool-1] c.codingmore.component.PublishPostTask : Fixed Rate Task : The time is now 2022-04-27 15:33:09
2022-04-27 15:33:13.295 INFO 4421 --- [led-task-pool-1] c.codingmore.component.PublishPostTask : Fixed Rate Task : The time is now 2022-04-27 15:33:13

About Cron Expressions
By the way, let’s popularize Cron expressions, which are often encountered in timed tasks. The word Cron comes from the Greek chronos, which means time.
Cron expression is a string with time meaning, separated by 5 spaces, divided into 6 time elements. A few examples will make it clear.

Example Description 0 15 10 ? * * Execute task 0 0 10,14,16 every day at 10:15 am * * ? Execute task 0 0 12 every day at 10:00, 14:00, 16:00 0 15 10 15 * ? Execute the task at 10:15 am on the 15th of each month
The syntax format of Cron can be summarized as:

Seconds Minutes Hours DayofMonth Month DayofWeek

The value range of each time element and the special characters that can appear are shown below.

Special characters that can appear in the value range of the time element seconds[0,59]*,-/minutes[0,59]*,-/hours[0,59]*,-/dates[0,31]*,-/ ?LWMonth[1,12]*,-/Week[1,7]*,-/?L#
The meanings and examples of special characters are shown below.

Examples of the meaning of special characters *All possible values ​​are well understood, the month field is each month, the week field is each day of the week, and the enumerated values ​​are well understood, 10, 14, 16 in the hour field, which means these few Hours are optional - the range is well understood. In the minute field, 10-19 means that 10-19 minutes are executed every one minute / the increment of the specified value is well understood. In the minute field, 0/15 means that every Execute every 15 minutes Domain support, indicating the last day of the month or the last day of the week W workdays other than weekends are well understood, only supported by the date domain #The first few days of each month are well understood, only the week domain is supported, 4#2 Represents the second Thursday of a month
About Quartz
Quartz is a powerful open source task scheduling framework that has accumulated 5k+ stars on GitHub. From small stand-alone applications to large distributed applications, Quartz can be integrated.

Before using Quartz, let's clarify 4 core concepts:

Job: task, the specific content to be executed.
JobDetail: The details of the task, Job is the content to be executed, and also includes the strategy and scheme of this task scheduling.
Trigger: Trigger, you can specify the time of task execution through Cron expression.
Scheduler: A scheduler that can register multiple JobDetails and Triggers to schedule, suspend and delete tasks.

Related Articles

Explore More Special Offers

  1. Short Message Service(SMS) & Mail Service

    50,000 email package starts as low as USD 1.99, 120 short messages start at only USD 1.00