This article is participating in the Java Theme Month – Java Debug Notes EventActive link

Based on the annotation

Start by annotating @enablesCheduling in the startup class

@SpringBootApplication
@EnableScheduling  // Enable a scheduled task
public class TasksApplication {

    public static void main(String[] args) { SpringApplication.run(TasksApplication.class, args); }}Copy the code

Create CronController class

@RestController
public class CronController {

    // Add a scheduled task
    @Scheduled(cron = "0/5 * * * * ?" )
    public void configureTasks(a) {
        System.err.println("Perform scheduled tasks:" + SimpleDateFormat.getDateTimeInstance().format(newDate())); }}Copy the code

Then start the project and the console will proceed to the current time.” 0/5 * * * *?” Indicates that the command is executed every five seconds.

Cron expression

Cron has seven digits in total, the last digit is year, which can be left blank, so we can write six digits:

  • The first digit is seconds. The value ranges from 0 to 59

  • The second value is the minute, ranging from 0 to 59

  • The third digit indicates the hour. The value ranges from 0 to 23

  • The fourth digit indicates the day and the value ranges from 1 to 31

  • The fifth digit indicates the month. The value ranges from 1 to 12

  • The value ranges from 1 to 7. 1 indicates Sunday, and 2 indicates Monday.

  • The value 7 indicates the year and can be left blank. The value ranges from 1970 to 2099

The most difficult to understand are the symbols. Here’s an example of each symbol:

Universal symbol

, : lists enumeration values. For example, if the second value is 5,35, the enumeration value is executed when the number of minutes is 5 or 35. – : indicates the range. For example, if the second value is 5-35, the value is executed every minute in the range of 5 to 35 minutes. * : matches any value of the field. For example, the second * indicates that the number of minutes is unlimited. / : Indicates that the command is executed at a specified time. For example, if 5/6 is used in the second digit, the command is executed once when the number of minutes is 5 and again every 6 minutes, that is, at 11 and 17 minutes.

Proprietary symbol

? Can only be used in the fourth (day) and sixth (week) fields, because the two fields are mutually exclusive and one must be set? .

L: At the end. Can only be used in the fourth (day) and sixth (week) fields. If 5L is used in the sixth, it means that it is executed on the last Thursday (1 for Sunday, 2 for Monday).

W: indicates a valid working day (Monday to Friday) and can only appear in the fourth digit (day) field. The system fires the event on the most recent valid business day to the specified date. If 15W is the closest working day to the 15th, it could be the 15th (which happens to be a working day) then it’s executed on the 15th. If the 15th is not a working day, it is a Sunday, then it is postponed and implemented on the 16th. The 16th is the date closest to a working day.

LW: indicates the last working day of a month.

#: used to determine the day of the week of a month, can only appear in the sixth-place (week) field, for example, 4#3, indicating the third Wednesday of a month.

C:: can only be used in the fourth (day) and sixth (week) fields, which need to be associated with the calendar. If there is no association, it can be ignored.

0 0 2 1*? * indicates the monthly value1Day early in the morning2Point to perform0 15 10 ? * 6L 2002-2006said2002-2006On the last Friday morning of every month in nineteen seventeen10:15perform0 0 10.14.16* *? Every morning10Points, in the afternoon2Points,4Point to perform0 0/30 9-17* *? Every half hour during nine to five working hours"0, 0, 12 * *?Every day at noon12Some trigger"0, 15, 10 * *? *"Every morning10:15The trigger"0 * 14 * *?"Every afternoon2Point to the afternoon2:59During each of the1Minutes to trigger"0 0/5 14 * *?"Every afternoon2Point to the afternoon2:55During each of the5Minutes to trigger"0, 15, 10, 15 times?"A month15The morning of10:15The trigger"0 15 10 L * ?"The morning of the last day of the month10:15The trigger"0 15 10? * 6L"Last Friday morning of every month10:15The triggerCopy the code

Pay attention to

Special symbols divided by? Not supported in spirng scheduled tasks. test

 @Scheduled(cron = "* * 20 ? * 1 # 3 ")
    public void configureTasks1(a) {
        System.err.println("Perform scheduled tasks:" + SimpleDateFormat.getDateTimeInstance().format(new Date()));
    }
Copy the code

There will be exceptions when the project starts

Caused by: java.lang.IllegalStateException: Encountered invalid @Scheduled method 'configureTasks1': For input string: "1 # 3"
Copy the code

Where can I use it?QuartzSupport.Online Cron expression generatorYou can test it.

Asynchronous multithreading implementation

Scheduled tasks are single-threaded by default. If a task lasts for a long time, subsequent scheduled tasks are delayed, resulting in task loss.

test

 @Scheduled(cron = "0/1 * * * * ?" )
    public void configureTasks(a) throws InterruptedException {
        System.out.println("Thread name 111:" + Thread.currentThread().getName() +"= = =" + SimpleDateFormat.getDateTimeInstance().format(new Date()));
        Thread.sleep(3000);
        System.err.println("Execute scheduled task 111:" + SimpleDateFormat.getDateTimeInstance().format(new Date()));
    }

    @Scheduled(cron = "0/1 * * * * ?" )
    public void configureTasks2(a) throws InterruptedException {
        System.out.println("Thread name 2222:" + Thread.currentThread().getName() +"= = =" + SimpleDateFormat.getDateTimeInstance().format(new Date()));
        System.err.println("Executive 222:" + SimpleDateFormat.getDateTimeInstance().format(new Date()));
    }
Copy the code

It can be seen that the two scheduled tasks are executed by the same thread. If one of the scheduled tasks takes time to execute (for example, configureTasks takes time to simulate and enables thread sleep), the task will be lost.

Enable asynchronous annotations

Annotate the startup class

@EnableAsync  
Copy the code

Setting asynchronous execution

@Scheduled(cron = "0/1 * * * * ?" )
    @Async
    public void configureTasks(a) throws InterruptedException {
        System.out.println("Thread name 111:" + Thread.currentThread().getName() + "= = = ="+ SimpleDateFormat.getDateTimeInstance().format(new Date()));
        Thread.sleep(2000);

    }
    @Scheduled(cron = "0/1 * * * * ?" )
    @Async
    public void configureTasks2(a) throws InterruptedException {
        System.out.println("Thread name 2222:" + Thread.currentThread().getName() + "= = = =" + SimpleDateFormat.getDateTimeInstance().format(new Date()));
        //Thread.sleep(1000);
    }
Copy the code

As you can see from the results, the configureTasks2 method is not affected by the slow processing of the configureTasks method. And each scheduled task is executed by a different thread. Task-xxx (the default) is the thread name. In actual development, we will customize the asynchronous thread pool, manage the number of threads properly, and define thread names to facilitate troubleshooting.

Create the AsyncConfig configuration class

@Configuration
public class AsyncConfig {

    // Maximum number of threads
    private static final int MAX_POOL_SIZE = 5;
    // Number of core threads
    private static final int CORE_POOL_SIZE = 2;
    // Queue capacity
    private static final int QUEUE_CAPACITY = 3;

    @Bean("testTaskExecutor")
    public AsyncTaskExecutor taskExecutor(a) {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
        taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
        taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
        // Active time
        taskExecutor.setKeepAliveSeconds(60);
        // Thread name prefix
        taskExecutor.setThreadNamePrefix("test-async-task-thread-pool-");
        returntaskExecutor; }}Copy the code

Modify the CronController class methods

@Scheduled(cron = "0/1 * * * * ?" )
    @Async("testTaskExecutor")
    public void configureTasks(a) throws InterruptedException {
        System.out.println("Thread name 111:" + Thread.currentThread().getName() +"= = =" + SimpleDateFormat.getDateTimeInstance().format(new Date()));
        Thread.sleep(3000);
        System.err.println("Execute scheduled task 111:" + SimpleDateFormat.getDateTimeInstance().format(new Date()));
    }

    @Scheduled(cron = "0/1 * * * * ?" )
    @Async("testTaskExecutor")
    public void configureTasks2(a) throws InterruptedException {
        System.out.println("Thread name 2222:" + Thread.currentThread().getName() +"= = =" + SimpleDateFormat.getDateTimeInstance().format(new Date()));
        System.err.println("Executive 222:" + SimpleDateFormat.getDateTimeInstance().format(new Date()));
    }
Copy the code

@async (“testTaskExecutor”) specifies the thread pool name.

  • If you have any questions or errors in this article, please feel free to comment. If you find this article helpful, please like it and follow it.