How many of the six implementations of timed tasks in java do you know?

Time:2022-5-24

In almost all projects, the use of scheduled tasks is indispensable. If used improperly, it will even cause asset damage. I still remember that many years ago, when I was working in the financial system, the disbursement business was to make payments through scheduled tasks. At that time, due to the limited processing capacity of the bank interface and the improper use of scheduled tasks, a large number of repeated disbursement requests were sent out. Fortunately, in the later link, the transaction card was in the system, and there was no asset loss.

Therefore, it is very necessary to systematically learn about timed tasks. This article will take you through several common timed task implementations in the Java field.

Thread waiting for implementation

First from the most primitive and simplest way to explain. You can create a thread first, and then let it run all the time in the while loop. You can achieve the effect of scheduled tasks through the sleep method.

public class Task {

    public static void main(String[] args) {
        // run in a second
        final long timeInterval = 1000;
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("Hello !!");
                    try {
                        Thread.sleep(timeInterval);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
    }
}

This method is simple and direct, but the functions that can be realized are limited, and they need to be realized by themselves.

JDK comes with timer implementation

At present, the timer API provided by JDK is the oldest way to implement timed tasks. Timer is a timer tool used to schedule the execution of specified tasks in a background thread. It can schedule tasks to be “executed once” or “executed multiple times” on a regular basis.

In the actual development, some periodic operations are often required, such as executing an operation every 5 minutes. The most convenient and efficient way to implement this operation is to use Java util. Timer tool class.

Core method

The core methods of timer class are as follows:

//Executes the specified task after the specified delay time
schedule(TimerTask task,long delay);

//Perform the specified task at the specified time. (execute only once)
schedule(TimerTask task, Date time);

//After delaying the specified time (delay), start to repeat the specified task at the specified interval (period)
schedule(TimerTask task,long delay,long period);

//Start at the specified time and repeat the specified task at the specified interval
schedule(TimerTask task, Date firstTime , long period);

//Start repeating tasks at a fixed rate at a specified time
scheduleAtFixedRate(TimerTask task,Date firstTime,long period);

//Start repeating the fixed rate execution task after the specified delay
scheduleAtFixedRate(TimerTask task,long delay,long period);

//Terminate this timer and discard all currently scheduled tasks.
cancal();

//Remove all cancelled tasks from the task queue of this timer.
purge();

Use example

Here are a few examples to demonstrate the use of the core method. First, define a general TimerTask class to define the tasks to be executed with.

public class DoSomethingTimerTask extends TimerTask {

    private String taskName;

    public DoSomethingTimerTask(String taskName) {
        this.taskName = taskName;
    }

    @Override
    public void run() {
        System. out. Println (New date() + ": task" + taskname + "" is executed. ");
    }
}

Specifies a delay in execution

Execute once after the specified delay time. This is a common scenario. For example, after the system initializes a component, delay it for a few seconds, and then execute the scheduled task.

public class DelayOneDemo {

    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new DoSomethingTimerTask("DelayOneDemo"),1000L);
    }
}

Execute the above code, execute the scheduled task after a delay of one second, and print the results. The second parameter is in milliseconds.

Fixed interval execution

The scheduled task starts to be executed at the specified delay time, and the scheduled task is executed at a fixed interval. For example, the execution is delayed by 2 seconds and the fixed execution interval is 1 second.

public class FixedRateDemo {

    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new DoSomethingTimerTask("FixedRateDemo"),2000L,1000L);
    }
}

When executing the program, you will find that it will be executed every 1 second after 2 seconds.

Fixed rate execution

Start to execute the scheduled task at the specified delay time, and the scheduled task is executed at a fixed rate. For example, the execution is delayed for 2 seconds and the fixed rate is 1 second.

public class FixedRateDemo {

    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new DoSomethingTimerTask("FixedRateDemo"),2000L,1000L);
    }
}

When executing the program, you will find that it will be executed every 1 second after 2 seconds.

At this time, do you wonder why schedule and scheduleatfixedrate have the same effect? Why do they provide two methods and what are the differences between them?

The difference between schedule and scheduleatfixedrate

Before understanding the differences between schedule and scheduleatfixedrate methods, look at their similarities:

  • Task execution did not time out. Next execution time = last execution start time + period;

  • Task execution timeout, next execution time = last execution end time;

When the task execution is not timed out, they are the last execution time plus the interval time to execute the next task. When the execution exceeds the time limit, it is executed immediately.

The difference between them lies in the different emphasis. The schedule method focuses on maintaining the stability of interval time, while the scheduleatfixedrate method focuses more on maintaining the stability of execution frequency.

Schedule focuses on keeping the interval stable

The schedule method will delay the following scheduled tasks due to the delay of the previous task. The calculation formula is scheduledExecutionTime (n + 1st time) = realexecutiontime (n time) + periodtime.

That is to say, if the execution time of the nth task is too long for some reason, and the systemcurrenttime > = scheduled execution time (n + 1 time) after the execution is completed, the N + 1 task will be executed immediately without interval waiting at this time.

In the next N + 2nd time, the scheduled execution time (n + 2nd time) of the task becomes real execution time (n + 1st time) + periodtime. This method pays more attention to maintaining the stability of interval time.

Scheduleatfixedrate keeps the execution frequency stable

When scheduleatfixedrate repeatedly executes the plan of a task, the scheduled execution time of each task is initially determined, that is, scheduledExecutionTime (nth time) = firstexecutetime + n * periodtime.

If the execution time of the nth task is too long for some reason, and the systemcurrenttime > = scheduledExecutionTime (n + 1st time) after the execution is completed, the interval wait will not be done at this time, and the nth + 1st task will be executed immediately.

The scheduled execution time (n + 2) of the next N + 2 task is still the first execution time + (n + 2) * periodtime, which is determined when the task is executed for the first time. To put it bluntly, this method pays more attention to maintaining the stability of execution frequency.

If one sentence is used to describe the difference between schedule and scheduleatfixedrate after task execution timeout, it is: the strategy of schedule is to miss if you miss, and follow the new rhythm; The strategy of scheduleatfixedrate is to try to catch up with the original rhythm if you miss it.

Defects of timer

Timer timer can execute tasks regularly (specify the time to execute tasks), delay (delay the execution of tasks by 5 seconds), and periodically (execute tasks every 1 second). However, timer has some defects. Firstly, timer’s support for scheduling is based on absolute time rather than relative time, so it is very sensitive to the change of system time.

Secondly, the timer thread will not catch exceptions. If an unchecked exception is thrown by the TimerTask, the timer thread will terminate. At the same time, the timer will not resume the execution of the thread. It will mistakenly think that the whole timer thread will be cancelled. At the same time, TimerTask that has been scheduled but not yet executed will not be executed, and new tasks cannot be scheduled. Therefore, if TimerTask throws an unchecked exception, timer will produce unpredictable behavior.

JDK comes with scheduledexecutorservice

Scheduledexecutorservice is a new scheduled task interface after Java 1.5. It is a scheduled task class designed based on thread pool. Each scheduled task will be assigned to a thread in the thread pool to execute. In other words, tasks are executed concurrently and do not affect each other.

It should be noted that the scheduledexecutorservice will actually start a thread only when the scheduled task is executed, and the scheduledexecutorservice is in the state of polling the task for the rest of the time.

Scheduledexecutorservice mainly has the following four methods:

ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
<V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);
ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnitunit);
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnitunit);

Among them, scheduleatfixedrate and schedulewithfixeddelay are more convenient and used in the implementation of timing programs.

The four interface methods defined in scheduledexecutorservice are almost the same as the corresponding methods in timer, except that the scheduled method of timer needs to pass in an abstract task of TimerTask externally. The scheduledexecutorservice is encapsulated in more detail. A layer of encapsulation will be made inside runnable or callable to encapsulate an abstract task class (scheduledfuturetask) similar to TimerTask. Then, the thread pool is passed in and the thread is started to execute the task.

Scheduleatfixedrate method

The scheduleatfixedrate method executes a task at a specified frequency and cycle. Definition and Parameter Description:

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
    long initialDelay,
    long period,
    TimeUnit unit);

Parameter corresponding meaning: Command refers to the thread to be executed; Initialdelay is the delayed execution time after initialization; Period is the minimum interval between two starts of execution; Unit is the timing unit.

Usage example:

public class ScheduleAtFixedRateDemo implements Runnable{

    public static void main(String[] args) {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        executor.scheduleAtFixedRate(
                new ScheduleAtFixedRateDemo(),
                0,
                1000,
                TimeUnit.MILLISECONDS);
    }

    @Override
    public void run() {
        System. out. Println (New date() + ": the task" scheduleatfixedratedemo "is executed.");
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

The above is the basic usage of the scheduleatfixedrate method, but when executing the program, you will find that it is not executed every 1 second, but every 2 seconds.

This is because scheduleatfixedrate executes tasks at intervals of period. If the task execution time is less than period, the next task will be executed after the interval of period after the last task is completed; However, if the task execution time is greater than period, the next task will be started immediately after the last task is executed.

Schedulewithfixeddelay method

The schedulewithfixeddelay method executes a task at a specified frequency interval. Definition and Parameter Description:

public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
    long initialDelay,
    long delay,
    TimeUnit unit);

Parameter corresponding meaning: Command refers to the thread to be executed; Initialdelay is the delayed execution time after initialization; Period is the interval from the end of the previous execution to the beginning of the next execution (interval execution delay time); Unit is the timing unit.

Usage example:

public class ScheduleAtFixedRateDemo implements Runnable{

    public static void main(String[] args) {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        executor.scheduleWithFixedDelay(
                new ScheduleAtFixedRateDemo(),
                0,
                1000,
                TimeUnit.MILLISECONDS);
    }

    @Override
    public void run() {
        System. out. Println (New date() + ": the task" scheduleatfixedratedemo "is executed.");
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

The above is the basic usage of the schedulewithfixeddelay method, but when executing the program, you will find that it is not executed every 1 second, but every 3 seconds.

This is because no matter how long the task is executed, the schedulewithfixeddelay will wait for the last task to be executed and then delay the next task to be executed.

Quartz framework implementation

In addition to the JDK’s own API, we can also use open source frameworks, such as quartz.

Quartz is an open source project in the field of job scheduling. Quartz can be used alone or integrated with spring framework. The latter is generally used in actual development. Using quartz, you can develop one or more scheduled tasks. Each scheduled task can be executed at a separate time, such as every 1 hour, at 10 a.m. on the first day of each month, at 5 p.m. on the last day of each month, and so on.

Quartz usually consists of three parts: scheduler, JobDetail and trigger, including simpletrigger and crontrigger. The following is a specific example.

Quartz integration

To use quartz, you first need to introduce corresponding dependencies into the POM file of the project:

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
    <version>2.3.2</version>
</dependency>

Define the job to execute the task. Here, you need to implement the job interface provided by quartz:

public class PrintJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System. out. Println (New date() + ": the task" printjob "is executed.");
    }
}

Create schedulers and triggers and perform scheduled tasks:

public class MyScheduler {

    public static void main(String[] args) throws SchedulerException {
        //1. Create scheduler
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        //2. Create a JobDetail instance and bind it to the printjob class (job execution content)
        JobDetail jobDetail = JobBuilder.newJob(PrintJob.class)
                .withIdentity("job", "group").build();
        //3. Build trigger instance and execute it every 1s
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger", "triggerGroup")
                . startnow() // effective immediately
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        . withintervalinseconds (1) // execute every 1s
                        .repeatForever()). build();// Always execute

        //4. The scheduler binds job and trigger and executes
        scheduler.scheduleJob(jobDetail, trigger);
        System.out.println("--------scheduler start ! ------------");
        scheduler.start();
    }
}

When executing the program, you can see that the scheduled task is executed every 1 second.

In the above code, job is the interface of quartz, and the implementation of business logic is realized by implementing this interface.

JobDetail binds the specified job. Each time the scheduler schedules the execution of a job, it will first get the corresponding job, then create the job instance, and then execute the execute() content in the job. After the task is executed, the associated job object instance will be released and cleared by the JVM GC.

Trigger is a trigger of quartz, which is used to inform the scheduler when to execute the corresponding job. Simpletrigger can execute a job task once in a specified time period or multiple times in a time period.

Crontrigger has very powerful functions. It is a calendar based job scheduling, while simpletrigger precisely specifies the interval, so crotrigger is more commonly used than simpletrigger. Crotrigger is based on cron expression.

Examples of common cron expressions are as follows:

It can be seen that crontrigger based on quartz can realize very rich timed task scenarios.

Spring Task

Starting from spring 3, spring comes with a set of timed task tool spring task, which can be regarded as a lightweight quartz. It is very simple to use. In addition to spring related packages, there is no need for additional packages. It supports two forms: annotations and configuration files. Generally, in the spring system, for simple scheduled tasks, you can directly use the functions provided by spring.

The form based on XML configuration file is no longer introduced. Let’s look directly at the implementation based on annotation. It’s very simple to use. You can use the code directly:

@Component("taskJob")
public class TaskJob {

    @Scheduled(cron = "0 0 3 * * ?")
    public void job1() {
        System. out. Println ("scheduled task defined by cron");
    }

    @Scheduled(fixedDelay = 1000L)
    public void job2() {
        System. out. Println ("scheduled task defined by fixeddelay");
    }

    @Scheduled(fixedRate = 1000L)
    public void job3() {
        System. out. Println ("scheduled task defined by fixedrate");
    }
}

If you are in the spring boot project, you need to add @ enableshcheduling to the startup class to start the scheduled task.

In the above code, @ component is used to instantiate the class, which is independent of the scheduled [email protected] Scheduled specifies that the method is executed based on scheduled tasks, and the specific execution frequency is determined by the expression specified by cron. The cron expression is consistent with the expression used by crontrigger. In contrast to cron, spring also provides two forms of timed task execution: fixeddelay and fixedrate.

Difference between fixeddelay and fixedrate

The difference between “timer” and “delay” is very similar.

Fixedrate has a concept of timetable. When the task is started, T1, T2 and T3 have scheduled the execution time, such as 1 minute, 2 minutes and 3 minutes. When the execution time of T1 is greater than 1 minute, T2 will be delayed. When T1 is completed, T2 will execute immediately.

Fixeddelay is relatively simple, indicating the time interval from the end of the previous task to the beginning of the next task. The interval between the two tasks is always the same no matter how long the task takes to execute.

Disadvantages of spring task

Spring task itself does not support persistence, nor does it launch the official distributed cluster mode. It can only be implemented by developers manually in business applications, which can not meet the needs of visualization and easy configuration.

Distributed task scheduling

The above scheduled task schemes are for single machines and can only be used in a single JVM process. Now it is basically a distributed scenario, which requires a set of distributed task scheduling framework with high performance, high availability and scalability in a distributed environment.

Quartz distributed

First of all, quartz can be used in distributed scenarios, but it needs to be based on the form of database lock. In short, the distributed scheduling strategy of quartz is an asynchronous strategy with database as the boundary. Each scheduler follows an operation rule based on database lock, which ensures the uniqueness of the operation. At the same time, the asynchronous operation of multiple nodes ensures the reliability of the service.

Therefore, quartz’s distributed solution only solves the problem of high availability of tasks (reducing single point of failure). The bottleneck of processing capacity lies in the database, and there is no task segmentation at the execution level, which can not maximize the efficiency. It can only rely on the shedulex scheduling level for segmentation, but the scheduling layer for parallel segmentation is difficult to do the optimal segmentation in combination with the actual operation resources.

Lightweight artifact XXL job

Xxl-job is a lightweight distributed task scheduling platform. It is characterized by platform, easy deployment, rapid development, simple learning, lightweight and easy expansion. The scheduling center and actuator function complete the execution of scheduled tasks. The dispatching center is responsible for unified dispatching, and the actuator is responsible for receiving and executing the dispatching.

For small and medium-sized projects, this framework is used more.

Other frameworks

In addition, there are elastic job, Saturn, sia-task, etc.

Elastic job has high availability and is a distributed scheduling solution.

Saturn is an open source distributed task scheduling platform of vipshop, which has been transformed on the basis of elastic job.

Sia-task is an open source distributed task scheduling platform of Yixin.

Summary

This paper combs the realization of six kinds of timed tasks. In terms of the application of practical scenarios, most systems have been separated from the single machine mode. For systems with low concurrency, XXL job may be a good choice.

Who can see through the essence of technology in an article,

And people who can’t see for a lifetime,

It’s destined to be a very different brick moving career.