多线程一直是工做或面试过程当中的高频知识点,今天给你们分享一下使用 ThreadPoolTaskExecutor 来自定义线程池和实现异步调用多线程。java
本文采用 Executors 的工厂方法进行配置。面试
在项目的 resources 目录下建立 executor.properties 文件,并添加以下配置:spring
# 异步线程配置 # 核心线程数 async.executor.thread.core_pool_size=5 # 最大线程数 async.executor.thread.max_pool_size=8 # 任务队列大小 async.executor.thread.queue_capacity=2 # 线程池中线程的名称前缀 async.executor.thread.name.prefix=async-service- # 缓冲队列中线程的空闲时间 async.executor.thread.keep_alive_seconds=100
@Configuration // @PropertySource是找的target目录下classes目录下的文件,resources目录下的文件编译后会生成在classes目录 @PropertySource(value = {"classpath:executor.properties"}, ignoreResourceNotFound=false, encoding="UTF-8") @Slf4j public class ExecutorConfig { @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; @Value("${async.executor.thread.keep_alive_seconds}") private int keepAliveSeconds; @Bean(name = "asyncTaskExecutor") public ThreadPoolTaskExecutor taskExecutor() { log.info("启动"); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 核心线程数 executor.setCorePoolSize(corePoolSize); // 最大线程数 executor.setMaxPoolSize(maxPoolSize); // 任务队列大小 executor.setQueueCapacity(queueCapacity); // 线程前缀名 executor.setThreadNamePrefix(namePrefix); // 线程的空闲时间 executor.setKeepAliveSeconds(keepAliveSeconds); // 拒绝策略 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 线程初始化 executor.initialize(); return executor; } }
**核心线程数:**线程池建立时候初始化的线程数。当线程数超过核心线程数,则超过的线程则进入任务队列。微信
**最大线程数:**只有在任务队列满了以后才会申请超过核心线程数的线程。不能小于核心线程数。多线程
**任务队列:**线程数大于核心线程数的部分进入任务队列。若是任务队列足够大,超出核心线程数的线程不会被建立,它会等待核心线程执行完它们本身的任务后再执行任务队列的任务,而不会再额外地建立线程。**举例:**若是有20个任务要执行,核心线程数:10,最大线程数:20,任务队列大小:2。则系统会建立18个线程。这18个线程有执行完任务的,再执行任务队列中的任务。app
**线程的空闲时间:**当 线程池中的线程数量 大于 核心线程数 时,若是某线程空闲时间超过 keepAliveTime ,线程将被终止。这样,线程池能够动态的调整池中的线程数。异步
**拒绝策略:**若是(总任务数 - 核心线程数 - 任务队列数)-(最大线程数 - 核心线程数)> 0 的话,则会出现线程拒绝。举例:( 12 - 5 - 2 ) - ( 8 - 5 ) > 0,会出现线程拒绝。线程拒绝又分为 4 种策略,分别为:async
一般 ThreadPoolTaskExecutor 是和 @Async 一块儿使用。在一个方法上添加 @Async 注解,代表是异步调用方法函数。@Async 后面加上线程池的方法名或 bean 名称,代表异步线程会加载线程池的配置。ide
@Component @Slf4j public class ThreadTest { /** * 每10秒循环一次,一个线程共循环10次。 */ @Async("asyncTaskExecutor") public void ceshi3() { for (int i = 0; i <= 10; i++) { log.info("ceshi3: " + i); try { Thread.sleep(2000 * 5); } catch (InterruptedException e) { e.printStackTrace(); } } } }
备注:必定要在启动类上添加 @EnableAsync 注解,这样 @Async 注解才会生效。函数
// 在启动类上添加 @EnableScheduling 注解 @SpringBootApplication @EnableScheduling public class SpringBootStudyApplication { public static void main(String[] args) { SpringApplication.run(SpringBootStudyApplication.class, args); } }
// @Component 注解将定时任务类归入 spring bean 管理。 @Component public class listennerTest3 { @Autowired private ThreadTest t; // 每1分钟执行一次ceshi3()方法 @Scheduled(cron = "0 0/1 * * * ?") public void run() { t.ceshi3(); } }
ceshi3() 方法调用线程池配置,且异步执行。
@Component @Slf4j public class ThreadTest { /** * 每10秒循环一次,一个线程共循环10次。 */ @Async("asyncTaskExecutor") public void ceshi3() { for (int i = 0; i <= 10; i++) { log.info("ceshi3: " + i); try { Thread.sleep(2000 * 5); } catch (InterruptedException e) { e.printStackTrace(); } } } }
经过继承 CommandLineRunner 类实现。
@Component public class ListennerTest implements CommandLineRunner { @Autowired private ThreadTest t; @Override public void run(String... args) { for (int i = 1; i <= 10; i++) { t.ceshi(); } } }
@Component @Slf4j public class ThreadTest { @Async("asyncTaskExecutor") public void ceshi() { log.info("ceshi"); } }
还能够经过接口的形式来异步调用多线程:
@RestController @RequestMapping("thread") public class ListennerTest2 { @Autowired private ThreadTest t; @GetMapping("ceshi2") public void run() { for (int i = 1; i < 10; i++) { t.ceshi2(); } } }
@Component @Slf4j public class ThreadTest { @Async("asyncTaskExecutor") public void ceshi2() { for (int i = 0; i <= 3; i++) { log.info("ceshi2"); } } }
@RunWith(SpringRunner.class) @SpringBootTest public class ThreadRunTest { @Autowired private ThreadTest t; @Test public void thread1() { for (int i = 1; i <= 10; i++) { t.ceshi4(); } } }
@Component @Slf4j public class ThreadTest { @Async("asyncTaskExecutor") public void ceshi4() { log.info("ceshi4"); } }
以上主要介绍了 ThreadPoolTaskExecutor 线程池的配置、使用、相关注解的意义及做用,也简单介绍了使用 @Async 来异步调用线程,最后又列举了多线程的使用场景,并配上了代码示例。但愿你们喜欢。
本文来自: 微信公众号【大数据实战演练】。阅读更多精彩好文,欢迎关注微信公众号【大数据实战演练】。