Thread Pool and Timing Task Function in Spring

Keywords: Java Spring JDK Attribute xml

1. Functional introduction

The Spring framework provides an abstract interface between thread pool and timed task execution: TaskExecutor and Task Scheduler to support asynchronous and timed task execution. At the same time, the abstract interface defined by the framework itself is used to shield the differences between the underlying JDK versions and between thread pools and timed task processing in Java EE.
Spring also supports the integration of timer Timer and Quartz Scheduler frameworks within JDK.

2. Thread pool abstraction: TaskExecutor

The related class diagrams of TaskExecutor are as follows:

The source code of the TaskExecutor interface is as follows:

public interface TaskExecutor extends Executor {

    /**
     * Execute the given {@code task}.
     * <p>The call might return immediately if the implementation uses
     * an asynchronous execution strategy, or might block in the case
     * of synchronous execution.
     * @param task the {@code Runnable} to execute (never {@code null})
     * @throws TaskRejectedException if the given task was not accepted
     */
    @Override
    void execute(Runnable task);

}

This interface is almost exactly the same as Executor. It only defines a method to receive Runnable parameters. According to Spring, this interface was originally designed to extract JKD from other builds when using threads. Among Spring's other components, such as Application Event Multicaster, Quartz uses TaskExecutor as an abstraction of thread pools.

3. Implementation class of TaskExecutor provided by Spring

org.springframework.core.task.SimpleAsyncTaskExecutor

This implementation supports asynchronous execution of tasks, but there is no thread reuse in this implementation. Each time a submitted task is executed, a new thread will be created. When the task is executed, the thread will be closed. The maximum concurrency number is unlimited by default, but the maximum concurrency number can be set by calling the following method. Thread pools are generally used to replace this implementation, especially when performing tasks with short lifecycles.

public void setConcurrencyLimit(int concurrencyLimit) {
        this.concurrencyThrottle.setConcurrencyLimit(concurrencyLimit);
    }

Spring also provides implementation classes for synchronous task execution:

  org.springframework.core.task.SyncTaskExecutor

There is only one method in this class. The code is as follows:

@Override
    public void execute(Runnable task) {
        Assert.notNull(task, "Runnable must not be null");
        task.run();
    }

This method directly calls the run method of the incoming Runable object, so when executing this method, no new threads will be opened, only ordinary method calls will synchronously execute the submitted Runable object.
Spring has two implementation classes of thread pool: SimpleThreadPool Task Executor and ThreadPool Task Executor. We use SimpleThreadPool Task Executor when we have the need for Quarts and non-Quarts to share the same thread pool. In addition to this, we usually use SimpleThreadPool Task Executor.
ThreadPoolTaskExecutor, an implementation that configures the configuration of thread pools through property injection. The source code for attribute injection in ThreadPoolTaskExecutor is as follows: This configuration can be modified at run time, and synchronization control is used in the process of code modification.

/**
 * Set the ThreadPoolExecutor's core pool size.
 * Default is 1.
 * <p><b>This setting can be modified at runtime, for example through JMX.</b>
 */
public void setCorePoolSize(int corePoolSize) {
    synchronized (this.poolSizeMonitor) {
        this.corePoolSize = corePoolSize;
        if (this.threadPoolExecutor != null) {
            this.threadPoolExecutor.setCorePoolSize(corePoolSize);
        }
    }
}

4.TaskExecutor uses Demo

First, define a task as follows:

public class DataSimulation implements Runnable{

  private HourAverageValueDao hourAverageValueDao;

  @Override
  public void run() {

      Random random = new Random();
      AverageValue averageValue = new AverageValue();
      averageValue.setAverageVaule(random.nextInt(100));
      averageValue.setCreatedTime(new Date());
      hourAverageValueDao.insert(averageValue);

  }
}

This task generates a random number, encapsulates it as a class object, and inserts the data into the database.
Then you need to specify a class, using TaskExecutor, with the following code:

public class DataFacotory {

    private TaskExecutor executor;

    public TaskExecutor getExecutor() {
        return executor;
    }

    public void setExecutor(TaskExecutor executor) {
        this.executor = executor;
    }

    public void dataFactory(){

        for (int i =0; i < 10; i++){
            executor.execute(new DataSimulation());
        }
    }
}

In this class, the attributes of TaskExecutor are defined and a method is defined. In this method, 10 tasks are submitted to TaskExecutor. Next, only the Spring file is configured and injected into TaskExecutor to realize the use of thread pool. The configuration file is as follows:

<bean id = "taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
      <property name="corePoolSize" value = "5"></property>
      <property name = "maxPoolSize" value="10"></property>
      <property name="queueCapacity" value="25"></property>
  </bean>

This thread pool can be used when the configuration is completed. The thread pool provided by Spring can configure the configuration of the thread pool through configuration files, which is a great advantage over the thread pool provided by JDk.

5. Why use thread pool

1. Reuse threads by using thread pools to reduce the overhead of creating and destroying threads
2. The task of executing threads is handed over to the thread pool to operate, which realizes decoupling in a sense.
3. Thread pool can control the maximum number of tasks concurrently, which plays an important role in preventing memory overflow and concurrent optimization.

6. Timing Task Abstraction Class: Task Scheduler

The source code of the Task Scheduler interface is as follows:

public interface TaskScheduler {
  // Use triggers to determine whether task is executed or not
ScheduledFuture schedule(Runnable task, Trigger trigger);
// Execute once at starttime
ScheduledFuture schedule(Runnable task, Date startTime);
Execute task once per period period from starttime
ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);
Execute every other period
ScheduledFuture scheduleAtFixedRate(Runnable task, long period);
Start Time and execute every delay for a long time
ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
Execute every delay time
ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);

}

The interface that specifies the start time, and if the time is a point in the past, the task will be executed immediately. The ways in which the above execution modes are passed into Trigger are the most frequently used. Only one method is defined in the Trigger interface:

Date nextExecutionTime(TriggerContext triggerContext);

The parameter type TriggerContext is defined as follows:

public interface TriggerContext {

/**
 * Return the last <i>scheduled</i> execution time of the task,
 * or {@code null} if not scheduled before.
 */
Date lastScheduledExecutionTime();

/**
 * Return the last <i>actual</i> execution time of the task,
 * or {@code null} if not scheduled before.
 */
Date lastActualExecutionTime();

/**
 * Return the last completion time of the task,
 * or {@code null} if not scheduled before.
 */
Date lastCompletionTime();

}

Provides an interface for retrieving last task execution information. We can implement custom triggers to execute tasks by implementing the Trigger interface. Of course Spring also provides two default implementation classes: Periodic Trigger and Ron Trigger.

7. Task Scheduler Timing Task Demo

First, enable the annotation configuration in the Spring configuration file as follows:

<task:annotation-driven scheduler="myScheduler"/> //Specifying the scheduler attribute is optional and can be used normally without adding it.
<task:scheduler id="myScheduler" pool-size="10"/>

Then create the service. And use @Scheduled annotation to create timed tasks in service The code is as follows:

@Component
public class SchedulerPoolTest {

  @Scheduled(cron = "0 * * * * ?")
  public void task1(){
      System.out.println("test");
      Thread thread =  Thread.currentThread();
      System.out.println("ThreadName:" + thread.getName() + ",id:" + thread.getId() + ",group:" + thread.getThreadGroup());

  }

  @Scheduled(fixedDelay = 5000)
  public void task2(){
      System.out.println("test");
      Thread thread =  Thread.currentThread();
      System.out.println("ThreadName:" + thread.getName() + ",id:" + thread.getId() + ",group:" + thread.getThreadGroup());

  }

}

Just adding the above content may not be able to execute the task properly, but the following two points need to be noted:

1. The SchedulerPoolTest class must be included in the package scanned by spring
The configuration is as follows:

<context:component-scan base-package="com.zjut.task" />

2. You need to add a spring configuration file listener in web.xml. The code is as follows:

<context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>classpath*:spring-task.xml</param-value>
 </context-param>

 <listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>

After adding the above content, the server will be started and the task will be executed regularly.

8.Cron expression

The Cron expression consists of six strings, each representing:

{seconds} {minutes} {hours} {days} {months} {weeks}

The allowable range of values for each string is:

Special Characters Allowed by Values Allowed by Field Names  
0-59 seconds,-*/  
Points 0-59,-*/  
Hours 0-23,-*/  
Days 1-31,-*?/L W C  
January-12 or JAN-DEC,-*/  
Weekly 1-7 or SUN-SAT, -*?/L C#

Commonly used Cron expressions:

"0 10, 44 14? 3 WED" is triggered at 2:10 and 2:44 p.m. on Wednesday, March each year.
"0/514, 18* *?" Triggers every five minutes between 2 p.m. and 2:55 p.m. and between 6 p.m. and 6:55 p.m.
"15-30/5* * * *?" Triggered between 15 and 30 seconds per minute, every 5 seconds.
"0 1510?* 5#3" Triggers tasks at 10:150 seconds on Thursday, the third week of each month

Note: Question marks are used to avoid conflicts between day and week settings. When one of them sets a specific value, the other must be used? Another link generated by a Cron expression is recommended:http://www.cronmaker.com/

9.@Async annotation

Async annotations provide the ability to call methods asynchronously. When the method annotated is called, the method caller returns immediately without waiting for the method to be executed. The called method allocates a thread from the thread pool to execute the method.

10. Problems with concurrent execution in Spring timing tasks

The same task, when the previous task has not been completed, the new task will not be executed.
For different tasks: TODO...

Posted by nakago on Sat, 25 May 2019 16:01:11 -0700