线程池使用场景
在执行异步任务或者并发任务的时候,我们往往用new Thread()
方法来创建新的线程,这样弊端较多,如频繁的创建销毁线程占用大量系统资源,线程上线不可控,资源耗尽的风险,这时候可以引入线程池就比较合理,使用线程池的优势有哪些:
使用线程池优点:
1. 降低系统系统资源消耗,重用已有的线程,减小创建销毁线程的资源消耗。
2. 提高系统响应速度,任务达到就有线程执行,无需等待新线程的创建。
3. 方便线程并发数量的管控,线程若是无限制的创建,不仅会额外消耗大量系统资源,更是占用过多资源而阻塞系统,从而降低系统的稳定性。线程池能有效管控线程,统一分配、调优,提供资源使用率;
4. 线程池它有自己独特的功能,如定时,可控线程数量,使用简单方便。
1.0 线程池的实现方式
1.1 定义一个线程池,并 @EnableAsync 开启异步,在需要异步执行的方法上增加 @Async
1.2 实现 AsyncConfigurer 接口,在需要异步支持的方法上使用 @Async
下面逐一实现这两种方式
task:
pool:
corePoolSize: 5
maxPoolSize: 20
keepAliveSeconds: 300
queueCapacity: 50
package com.fei.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Data
@EnableAsync
@Configuration
@ConfigurationProperties(prefix = "task")
public class ExecutorConfig {
@Value("${task.pool.corePoolSize}")
private int corePoolSize;
@Value("${task.pool.maxPoolSize}")
private int maxPoolSize;
@Value("${task.pool.queueCapacity}")
private int queueCapacity;
@Value("${task.pool.keepAliveSeconds}")
private int keepAliveSeconds;
private static String NAME_PREFIX = "thread-call-runner-%d-";
@Bean(name = "asyncServiceExecutor")
public ThreadPoolTaskExecutor asyncServiceExecutor(){
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(corePoolSize);
threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize);
threadPoolTaskExecutor.setQueueCapacity(queueCapacity);
threadPoolTaskExecutor.setKeepAliveSeconds(keepAliveSeconds);
threadPoolTaskExecutor.setThreadNamePrefix(NAME_PREFIX);
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
//加载
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
进行测试
package com.fei.controller;
import com.fei.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
public class ThreadController {
@Autowired
private AsyncService asyncService;
@Autowired
private ThreadPoolTaskExecutor threadPool;
@RequestMapping("test")
public void testAsync() throws InterruptedException {
System.out.println("testAsync 执行开始");
TimeUnit.SECONDS.sleep(2);
asyncService.executeAsync();
asyncService.executeAsync();
System.out.println("testAsync 执行完毕");
}
@RequestMapping("testThreadPool")
public void testThreadPool() throws InterruptedException {
System.out.println("testThreadPool start");
TimeUnit.SECONDS.sleep(2);
threadPool.execute(() -> System.out.println("threadPool testThreadPool"));
System.out.println("testThreadPool end");
}
}
package com.fei.service.impl;
import com.fei.service.AsyncService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncServiceImpl implements AsyncService {
private static final Logger logger = LoggerFactory.getLogger(AsyncServiceImpl.class);
@Async("asyncServiceExecutor")
@Override
public void executeAsync() {
logger.info("------开始执行异步任务------");
System.out.println("start executeAsync");
System.out.println("当前运行的线程名称:" + Thread.currentThread().getName());
for(int i=0 ;i<5000;i++){
System.out.println(Thread.currentThread().getName()+"----->"+i);
}
logger.info("end executeAsync");
System.out.println("------异步任务执行结束------");
}
}
测试
第二种方式给第一种方式差不多,主要实现AsyncConfigurer之后和第一种方法相同。
ThreadPoolExecutor 和ThreadPoolTaskExecutor的区别,后者对前者进行封装,ThreadPoolExecutor 属于jdk自带的,ThreadPoolTaskExecutor 属于spring自带的。
遇到的问题
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'executorConfig': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'task.poll.corePoolSize' in value "${task.poll.corePoolSize}"
发现是@Value注解中路径写错了pool写成poll了