代码结构自上而下
ThreadPoolConfig
package com.studymongo.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池配置类
* @author wangcp
* @date 2021/02/09 11:19
**/
@Data
@EnableAsync
@Configuration
@ConfigurationProperties(prefix = "poolconfig")
public class ThreadPoolConfig {
/**
* 线程池维护线程的最少数量,即使没有任务需要执行,也会一直存活
*/
private int corePoolSize;
/**
* 线程池维护线程的最大数量
*/
private int maxPoolSize;
/**
* 缓存队列(阻塞队列)当核心线程数达到最大时,新任务会放在队列中排队等待执行
*/
private int queueCapacity;
/**
* 允许的空闲时间,当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
*/
private int keepAlive;
@Bean
public TaskExecutor taskExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//设置核心线程数
executor.setCorePoolSize(corePoolSize);
// 设置最大线程数
executor.setMaxPoolSize(maxPoolSize);
// 设置队列容量
executor.setQueueCapacity(queueCapacity);
// 设置允许的空闲时间(秒)
executor.setKeepAliveSeconds(keepAlive);
// 设置默认线程名称
executor.setThreadNamePrefix("thread-");
// 设置拒绝策略rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALL_RUNS:不在新线程中执行任务,二十由调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后在关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
ThreadController
package com.studymongo.controller;
import com.studymongo.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 线程测试控制类
* @author wangcp
* @date 2021/02/09 11:32
**/
@RestController
public class ThreadController {
@Autowired
private AsyncService asyncService;
@GetMapping(value = "/thread")
public void thread(){
asyncService.executeAsync();
}
}
AsyncService
package com.studymongo.service;
public interface AsyncService {
void executeAsync();
}
AsyncServiceImpl
package com.studymongo.service.impl;
import com.studymongo.service.AsyncService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
* 实现类
* @author wangcp
* @date 2021/02/09 11:35
**/
@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {
@Async("taskExecutor")
@Override
public void executeAsync() {
log.info("start executeAsync");
try {
System.out.println("当前运行线的程名称:" + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
log.info("end executeAsync");
}
}
application.yaml
poolconfig:
corePoolSize: 3
maxPoolSize: 6
queueCapacity: 10
keepAlive: 100
运行结果
![](https://img.haomeiwen.com/i25773466/8acd65f8f6a1609f.png)
根据运行结果发现,线程运行时有序的!
线程池的各个参数详解
参数默认值及含义
# 线程池维护线程的最少数量,即使没有任务需要执行,也会一直存活
private int corePoolSize = 1;
# 线程池维护线程的最大数量
private int maxPoolSize = 2147483647;
# 允许的空闲时间,当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
private int keepAliveSeconds = 60;
# 缓存队列(阻塞队列)当核心线程数达到最大时,新任务会放在队列中排队等待执行
private int queueCapacity = 2147483647;
#拒绝策略
rejectedExecutionHandler = AbortPolicy()
对照源码解析,可以得出以下结论:
1.如果线程池中线程数量 < 核心线程数,新建一个线程执行任务;
2.如果线程池中线程数量 >= 核心线程数,则将任务放入任务队列
3.如果线程池中线程数量 >= 核心线程数 且 < maxPoolSize,且任务队列满了,则创建新的线程;
4.如果线程池中线程数量 > 核心线程数,当线程空闲时间超过了keepalive时,则会销毁线程;由此可见线程池的队列如果是无界队列,那么设置线程池最大数量是无效的;
5.如果线程池中的任务队列满了,而且线程数达到了maxPoolSize,并且没有空闲的线程可以执行新的任务,这时候再提交任务就会执行拒绝策略
拒绝策略详解
拒绝策略RejectedExecutionHandler分为以下5种
![](https://img.haomeiwen.com/i25773466/fa0136ef153ee108.png)
-
AbortPolicy
该策略是线程池的默认策略。使用该策略时,如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常
-
CallerRunsPolicy
使用此策略,如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行。就像是个急脾气的人,我等不到别人来做这件事就干脆自己干。
-
DiscardOldestPolicy
这个策略从字面上也很好理解,丢弃最老的。也就是说如果队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列。
因为队列是队尾进,队头出,所以队头元素是最老的,因此每次都是移除对头元素后再尝试入队。 -
DiscardPolicy
这个策略和AbortPolicy的slient版本,如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常。
-
自定义
如果以上策略都不符合业务场景,那么可以自己定义一个拒绝策略,只要实现RejectedExecutionHandler接口,并且实现rejectedExecution方法就可以了。具体的逻辑就在rejectedExecution方法里去定义就OK了。
public class MyRejectPolicy implements RejectedExecutionHandler{ public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { //Sender是我的Runnable类,里面有message字段 if (r instanceof Sender) { Sender sender = (Sender) r; //直接打印 System.out.println(sender.getMessage()); } } }
结论
这几种策略没有好坏之分,只是适用不同场景,具体哪种合适根据具体场景和业务需要选择,如果需要特殊处理就自己定义好了。
网友评论