线程池ThreadPoolTaskExcutor详解
1、前言
ThreadPoolTaskExecutor线程是Spring提供的线程池,其底层是依据JDK线程池ThreadPoolExecutor来实现的。
2、核心参数配置
- corePoolSize:线程池维护线程最小的数量,默认为1
- maxPoolSize:线程池维护线程最大数量,默认为Integer.MAX_VALUE
- keepAliveSeconds:(maxPoolSize-corePoolSize)部分线程空闲最大存活时间,默认存活时间是60s
- queueCapacity:阻塞任务队列的大小,默认为Integer.MAX_VALUE,默认使用LinkedBlockingQueue
- allowCoreThreadTimeOut:设置为true的话,keepAliveSeconds参数设置的有效时间对corePoolSize线程也有效,默认是flase
- threadFactory::用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。使用开源框架guava提供的ThreadFactoryBuilder可以快速给线程池里的线程设置有意义的名字
- rejectedExecutionHandler:拒绝策略,当队列workQueue和线程池maxPoolSize都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。
3、ThreadPoolTaskExcutor内部执行流程
- 通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务当一个任务通过execute(Runnable)方法欲添加到线程池时:
- 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
- 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
- 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
- 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
- 也就是:处理任务的优先级为:
- 核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
- 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
- - unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
4、代码
package com.taikang.sp.base.service.springconfig;
import com.taikang.sp.base.service.impl.task.TaskExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskRejectedException;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.concurrent.ListenableFuture;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
@Slf4j
public class ExecutorConfiguration {
/**
* 线程池总数
*/
@Value("${async.executor.thread.core_pool_size}")
private int corePoolSize;
/**
* 最大线程池数
*/
@Value("${async.executor.thread.max_pool_size}")
private int maxPoolSize;
/**
* 队列大小
*/
@Value("${async.executor.thread.queue_capacity}")
private int queueCapacity;
/**
* 线程名前缀
*/
@Value("${async.executor.thread.name.prefix}")
private String namePrefix;
@Bean(name = "asyncTaskExecutor")
public Executor asyncServiceExecutor() {
log.info("start asyncTaskExecutor");
ThreadPoolTaskExecutor executor = new CustomThreadPollTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(corePoolSize);
//配置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//配置队列大小
executor.setQueueCapacity(queueCapacity);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix(namePrefix);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
@Bean(initMethod = "executorInit")
public TaskExecutor taskExecutor() {
return new TaskExecutor();
}
@Slf4j
public static final class CustomThreadPollTaskExecutor extends ThreadPoolTaskExecutor {
private void showThreadPoolInfo(String prefix) {
ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
if (null == threadPoolExecutor) {
return;
}
log.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
this.getThreadNamePrefix(),
prefix,
threadPoolExecutor.getTaskCount(),
threadPoolExecutor.getCompletedTaskCount(),
threadPoolExecutor.getActiveCount(),
threadPoolExecutor.getQueue().size());
}
@Override
public void execute(Runnable task) {
showThreadPoolInfo("1. do execute");
super.execute(task);
}
@Override
public void execute(Runnable task, long startTimeout) {
showThreadPoolInfo("2. do execute");
super.execute(task, startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
showThreadPoolInfo("1. do submit");
return super.submit(task);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
showThreadPoolInfo("2. do submit");
return super.submit(task);
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
showThreadPoolInfo("1. do submitListenable");
return super.submitListenable(task);
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
showThreadPoolInfo("2. do submitListenable");
return super.submitListenable(task);
}
}
}
5、使用
使用1
@Resource(name = "asyncTaskExecutor")
private ThreadPoolTaskExecutor asyncTaskExecutor;
@RequestMapping(value = "/executor",method = RequestMethod.POST,produces = "applications/json;charset=utf-8")
public void test() throws ExecutionException, InterruptedException {
Random random = new Random();
List<Future<Integer>> list=new ArrayList<>();
int i=0;
while (i<10) {
list.add(asyncTaskExecutor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int anInt = random.nextInt(100);
System.out.println(Thread.currentThread().getName() + "====" + Thread.currentThread().getId() + anInt);
return anInt;
}
}));
i++;
}
for (Future future:list) {
Object o = future.get();
System.out.println(o+"=========="+"gzw");
}
}
使用2
/**
* 项目名称:spring-demo
* 类 名 称:TestExecutor
* 类 描 述:TODO
* 创建时间:2022/3/10 11:27 上午
*
* @author gzw
*/
@Component
@DependsOn({"springContextUtils"})
public class TestExecutor {
private final Map<Long,ExecutorMessage> messageMap;
public TestExecutor(){
ApplicationContext context = SpringContextUtils.getApplicationContext();
Assert.notNull(context,"ApplicationContext is null");
Map<String, ExecutorMessage> beans = context.getBeansOfType(ExecutorMessage.class);
Assert.isTrue(!CollectionUtils.isEmpty(beans),"user is not null");
messageMap=Collections.unmodifiableMap(beans.values().stream().collect(Collectors.toMap(ExecutorMessage::uniqueId,v->v)));
Assert.isTrue(!CollectionUtils.isEmpty(messageMap),"以id 分组的spring bean map 为空");
}
@Resource(name = "asyncTaskExecutor")
private ThreadPoolTaskExecutor asyncTaskExecutor;
@RequestMapping(value = "/executor",method = RequestMethod.POST,produces = "applications/json;charset=utf-8")
public void test() throws ExecutionException, InterruptedException {
Random random = new Random();
List<Future<User>> list=new ArrayList<>();
ExecutorMessage message = messageMap.get(0L).setUser(12);
list.add(asyncTaskExecutor.submit(message));
for (Future future:list) {
Object o = future.get();
System.out.println(o+"=========="+"gzw");
}
}
}
/**
* 项目名称:spring-demo
* 类 名 称:ExecutorMessage
* 类 描 述:TODO
* 创建时间:2022/3/10 7:54 下午
*
* @author gzw
*/
public abstract class ExecutorMessage implements Callable<User> {
/**
* 唯一标识
* @return
*/
public abstract Long uniqueId();
/**
* 真正执行的逻辑
* @param id
* @return
*/
public abstract User handler(Integer id);
private Integer id;
public ExecutorMessage setUser(Integer id){
this.id=id;
return this;
}
@Override
public User call() throws Exception {
return handler(id);
}
}
/**
* 项目名称:spring-demo
* 类 名 称:ExecutorHandler
* 类 描 述:TODO
* 创建时间:2022/3/10 8:04 下午
*
* @author gzw
*/
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) //多实例模式
public class ExecutorHandler extends ExecutorMessage{
@Override
public Long uniqueId() {
return 0L;
}
@Override
public User handler(Integer id) {
User user = new User();
user.setName("gzw").setId(id);
return user;
}
}
/**
* 项目名称:spring-demo
* 类 名 称:ExecutorFuture
* 类 描 述:TODO
* 创建时间:2022/3/10 9:05 下午
*
* @author gzw
*/
@RestController
@RequestMapping("/test")
public class ExecutorFuture {
@Autowired
private TestExecutor testExecutor;
@RequestMapping(value = "/executor",method = RequestMethod.POST,produces = "applications/json;charset=utf-8")
public void learn() throws ExecutionException, InterruptedException {
testExecutor.test();
System.out.println("结束");
}
}
6、总结
线程池提交任务有两种方法:
-
无返回值的任务使用public void execute(Runnable command) 方法提交;
子线程可能会在主线程结束之后才结束
而Runnable执行run时遇到异常并不会抛出
-
有返回值的任务使用public Future submit(Callable) 方法提交。
因为提交任务后有个取数据的过程,在从Future取数据的过程中,Callable自带的阻塞机制,这个机制保证主线程一定在子线程结束之后结束。
Callable执行call时遇到异常会抛出,