When you want to run long tasks in a web application, don't forget to use Spring's TaskExecutor, which helps you manage related components.
It's not uncommon to use threads in web applications, especially if you need to develop long-running tasks.
In Spring, we must pay special attention and use the tools it already provides, rather than using our own way of creating new threads.
We want our threads to be managed by Spring so that other components of the application can be used without any ill effects. We also want to shut down the application gracefully, rather than closing with some work unfinished.
Spring provides TaskExecutor
abstractions as task executors.
Spring's TaskExecutor
interface is the java.util.concurrent.Executor
same as an interface.
A number of pre-built implementations are included in the Spring distribution TaskExecutor
, and you can find out more about them from the official documentation .
By providing an TaskExecutor
implementation for the Spring environment, you will be able to TaskExecutor
inject beans into other beans and have access to managed threads:
package com.gkatzioura.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* Created by gkatzioura on 4/26/17.
*/
@Service
public class AsynchronousService {
@Autowired
private ApplicationContext applicationContext;
// 注入一个TaskExecutor实现
@Autowired
private TaskExecutor taskExecutor;
// 该方法是同步的,但是其调用taskExecutor.execute()中的run()会在另外一个
// 线程中异步执行
public void executeAsynchronously() {
taskExecutor.execute(new Runnable() {
@Override
public void run() {
// 这里是需要异步执行的长时间任务逻辑
}
});
}
}
To use it as above TaskExecutor
, the first step is to add the TaskExecutor
configuration to our Spring application:
package com.gkatzioura.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
/**
* Created by gkatzioura on 4/26/17.
*/
@Configuration
public class ThreadConfig {
// 定义一个类型为TaskExecutor的bean,方法名字无所谓
@Bean
public TaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(4);
executor.setThreadNamePrefix("default_task_executor_thread");
executor.initialize();
return executor;
}
}
Once we configure the executor (annotation: that is, the TaskExecutor
bean configured above), the task processing is very simple. We inject the actuator TaskExecutor
bean into another Spring component and submit the Runnable
object containing the task to be executed.
Because asynchronous code may need to interact with other components of the application, the best approach is to create prototype prototype
-scoped ( ) Runnable
bean instances and then inject them. Examples are as follows:
package com.gkatzioura;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
/**
* Created by gkatzioura on 10/18/17.
*/
@Component
@Scope("prototype")
public class MyThread implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(MyThread.class);
@Override
public void run() {
LOGGER.info("Called from thread");
}
}
We can then inject an executor (of type TaskExecutor
executor above) into our service and use it to execute Runnable
instances.
package com.gkatzioura.service;
import com.gkatzioura.MyThread;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* Created by gkatzioura on 4/26/17.
*/
@Service
public class AsynchronousService {
@Autowired
private TaskExecutor taskExecutor;
@Autowired
private ApplicationContext applicationContext;
public void executeAsynchronously() {
// 获取我们自定义的作用域为prototype的可执行任务Runnable bean
MyThread myThread = applicationContext.getBean(MyThread.class);
// 使用 taskExecutor对其进行异步执行
taskExecutor.execute(myThread);
}
}
In the next article , we'll take our multithreaded codebase to the next level by using Spring's asynchronous functions.
You can find the source code mentioned in this article on GitHub .