Springboot 使用线程池的两种方式

503 阅读3分钟

QQ_1721061351519.png

如上图所示,自定义线程池存在三种方式,其中前两种都是修改springboot 的默认线程池,第三种为完全的自定义线程池。

一. 修改默认线程池

1.1 修改默认线程池配置

如果没有配置线程池的话,springboot会自动配置一个ThreadPoolTaskExecutor 线程池到bean当中。 按如下配置,可修改默认线程池


# 核心线程数
spring.task.execution.pool.core-size=8
# 最大线程数
spring.task.execution.pool.max-size=16
# 空闲线程存活时间
spring.task.execution.pool.keep-alive=60s
# 是否允许核心线程超时
spring.task.execution.pool.allow-core-thread-timeout=true
# 线程队列数量
spring.task.execution.pool.queue-capacity=100
# 线程关闭等待
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=10
# 线程名称前缀
spring.task.execution.thread-name-prefix=custom-task-

1.2 自定义配置+实现spring默认线程池装配类+async注解

1.2.1 自定义配置

1. 修改application.properties
task.pool.corePoolSize=20
task.pool.maxPoolSize=40
task.pool.keepAliveSeconds=300
task.pool.queueCapacity=50
2. 配置类
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 线程池配置属性类
 * Created by Fant.J.
 */
@ConfigurationProperties(prefix = "task.pool")
public class TaskThreadPoolConfig {
    private int corePoolSize;

    private int maxPoolSize;

    private int keepAliveSeconds;

    private int queueCapacity;
    ...getter and setter methods...
}

1.2.2 实现默认线程池装配类

/**
 * 原生(Spring)异步任务线程池装配类
 * Created by Fant.J.
 */
@Slf4j
@Configuration
public class NativeAsyncTaskExecutePool implements AsyncConfigurer{


    //注入配置类
    @Autowired
    TaskThreadPoolConfig config;

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程池大小
        executor.setCorePoolSize(config.getCorePoolSize());
        //最大线程数
        executor.setMaxPoolSize(config.getMaxPoolSize());
        //队列容量
        executor.setQueueCapacity(config.getQueueCapacity());
        //活跃时间
        executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
        //线程名字前缀
        executor.setThreadNamePrefix("MyExecutor-");

        // setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务
        // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }


    /**
     *  异步任务中异常处理
     * @return
     */
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new AsyncUncaughtExceptionHandler() {

            @Override
            public void handleUncaughtException(Throwable arg0, Method arg1, Object... arg2) {
                log.error("=========================="+arg0.getMessage()+"=======================", arg0);
                log.error("exception method:"+arg1.getName());
            }
        };
    }
}

1.2.3 @Async 使用线程池

@Component
public class AsyncTask {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Async
    public void doTask2(int i) throws InterruptedException{
        logger.info("Task2-Native"+i+" started.");
    }
}

二. 自定义线程池

2.1 自定义配置

1. 修改application.properties

task.pool.corePoolSize=20
task.pool.maxPoolSize=40
task.pool.keepAliveSeconds=300
task.pool.queueCapacity=50

2. 配置类

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 线程池配置属性类
 * Created by Fant.J.
 */
@ConfigurationProperties(prefix = "task.pool")
public class TaskThreadPoolConfig {
    private int corePoolSize;

    private int maxPoolSize;

    private int keepAliveSeconds;

    private int queueCapacity;
    ...getter and setter methods...
}

2.2 自定义线程池

/**
 * 创建线程池
 * Created by Fant.J.
 */
@Configuration
@EnableAsync
public class TaskExecutePool {
    @Autowired
    private TaskThreadPoolConfig config;

    @Bean
    public Executor myTaskAsyncPool() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程池大小
        executor.setCorePoolSize(config.getCorePoolSize());
        //最大线程数
        executor.setMaxPoolSize(config.getMaxPoolSize());
        //队列容量
        executor.setQueueCapacity(config.getQueueCapacity());
        //活跃时间
        executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
        //线程名字前缀
        executor.setThreadNamePrefix("MyExecutor-");

        // setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务
        // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

2.3 修改启动类

给启动类添加注解

@EnableAsync
@EnableConfigurationProperties({TaskThreadPoolConfig.class} ) // 开启配置属性支持

2.4 使用自定义线程池

@Async + 线程池名

/**
 * Created by Fant.J.
 */
@Component
public class AsyncTask {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    //myTaskAsynPool即配置线程池的方法名,此处如果不写自定义线程池的方法名,会使用默认的线程池
    @Async("myTaskAsyncPool")
    public void doTask1(int i) throws InterruptedException{
        logger.info("Task"+i+" started.");
    }
}

3. 验证

那么如何验证当前线程池信息,确认自定义线程池是成功的呢?
参考:blog.51cto.com/u_16213424/…

4. 线程池配置准则

线程池配置分两种场景,一是cpu计算密集型,而是io密集型

4.1 计算密集型

n=cpu核心数
核心线程数=n+1
最大线程数=2n

4.2 io 密集型

n=cpu核心数
核心线程数=2n
最大线程数=4n