1.线程池工作特点:
1.线程池初始化时,线程数量为0(线程池采用的是懒加载方式,什么时候使用什么时候创建线程)
2.当线程池中的线程数未达到核心线程数时,只要来任务都会创建新线程
3.线程池中的线程数达到核心线程数后,当所有核心线程都在使用时,如果再来任务则将任务排入队列(任务数>=核心线程数+队列长度)
4.如果核心线程都在使用,队列也排满了,再来任务的话,创建新线程使用,直到线程池中的线程数达到最大值
5.如果线程池的线程数量达到了最大值,在来任务时,则拒绝
2.线程池拒绝策略
【1】什么时候会触发线程池的拒绝策略?
1.当我们调用 shutdown 等方法关闭线程池后,如果再向线程池内提交任务,就会遭到拒绝;
2.线程池没有空闲线程(线程达到最大线程数且都在执行任务)并且队列已经满;★★★
【2】拒绝策略类型有哪些?
线程池为我们提供了4种拒绝策略:
AbortPolicy
这种拒绝策略在拒绝任务时,会直接抛出一个类型为 RejectedExecutionException 的 RuntimeException,让你感知到任务被拒绝了,于是你便可以根据业务逻辑选择重试或者放弃提交等策略(默认)。
DiscardPolicy
当有新任务被提交后直接被丢弃掉,也不会给你任何的通知,相对而言存在一定的风险,因为我们提交的时候根本不知道这个任务会被丢弃,可能造成数据丢失。(不负责任)
DiscardOldestPolicy
丢弃任务队列中的头节点,通常是存活时间最长的任务,它也存在一定的数据丢失风险。
CallerRunsPolicy
第四种拒绝策略是 ,相对而言它就比较完善了,当有新任务提交后,如果线程池没被关闭且没有能力执行,则把这个任务交于提交任务的线程执行,也就是谁提交任务,谁就负责执行任务。
任务线程满了后,改策略可将执行的人为交换给主线程执行,这个过程相当于一个正反馈,此时如果主线程能处理,则处理,如果也不能处理,也就以为这当前服务不能接收新的任务了;
主线程处理任务期间,可以为线程池腾出时间,如果此时有新的空闲线程,那么继续协助主线程处理任务;
自定义拒绝策略
实现RejectedExecutionHandler接口来实现自己的拒绝策略;
3.验证线程池工作流程
1)环境准备
配置线程参数:
# 定时任务线程池基础参数
task:
pool:
corePoolSize: 5 # 核心线程数
maxPoolSize: 10 # 设置最大线程数
keepAliveSeconds: 2 # 设置线程活跃时间,单位秒
queueCapacity: 10 # 设置队列容量
配置模拟任务:
@Service
public class StockTimerService {
/**
* 拉取股票服务
*/
public void stockRtInto() {
//模拟网络I/O 1000毫秒
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2)并发情况(这里只测试任务数大于核心线程数)
并发任务数大于核心线程数 ,且小于等于核心线程数+任务队列长度
此时循环任务数为:15
代码:
/**
* 测试并发线程任务大于核心线程数+任务队列长度 且小于等于 最大线程数+任务队列长度
* @throws InterruptedException
*/
@Test
public void contextLoads3() throws InterruptedException {
//线程池初始化线程数为0 5--》核心线程处理 后10--》要入队列 最后的5:有新建的临时线程处理
log.info("线程池初始化大小:{}",threadPoolTaskExecutor.getPoolSize());
for (int i = 0; i < 20; i++) {
threadPoolTaskExecutor.execute(()->{
stockTimerService.stockRtInto();
});
//获取线程池内最新的线程数量
//发现在没有达到核心线程数时,哪怕有新的任务,也依旧开启新的线程执行
log.info("当前线池内的程数为:{}",threadPoolTaskExecutor.getPoolSize());
}
log.info("########任务线程构建完毕");
while (true) {
int queueSize = threadPoolTaskExecutor.getThreadPoolExecutor().getQueue().size();
log.info("当前阻塞队列任务数:{}" , queueSize);
log.info("当前活动线程数:{}" ,threadPoolTaskExecutor.getActiveCount());
long completedTaskCount = threadPoolTaskExecutor.getThreadPoolExecutor().getCompletedTaskCount();
log.info("线程池完成任务数:{}" ,completedTaskCount);
//当所有任务都完成后,那么completedTaskCount=taskCount
long taskCount = threadPoolTaskExecutor.getThreadPoolExecutor().getTaskCount();
log.info("总线池总任务数:{}" ,taskCount);
try {
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取线程池内最新的线程数量
log.info("当前线池内的程数为:{}",threadPoolTaskExecutor.getPoolSize());
log.info("############################");
}
}
效果:
并发任务数量超过最大线程数+任务队列时的场景:
定义循环线程数量:21,超过了1个线程;
代码:
@Test
public void contextLoads4() throws InterruptedException {
//线程池初始化线程数为0 5--》核心线程处理 || 后10--》要入队列 || 最后的5:有新建的临时线程处理 || 最后一个触发拒绝策略(默认抛出异常,终止程序)
log.info("线程池初始化大小:{}",threadPoolTaskExecutor.getPoolSize());
for (int i = 0; i < 21; i++) {
HashMap<Object, Object> map = new HashMap<>();
//模拟业务数据信息
map.put("type","handler stock");
map.put("size",10);
map.put("suss_"+i,"error");
// threadPoolTaskExecutor.execute(new MyStockRunable(map,stockTimerService));
threadPoolTaskExecutor.execute(new MyStockRunable(map,stockTimerService));
//获取线程池内最新的线程数量
//发现在没有达到核心线程数时,哪怕有新的任务,也依旧开启新的线程执行
log.info("当前线池内的程数为:{}",threadPoolTaskExecutor.getPoolSize());
}
log.info("########任务线程构建完毕");
while (true) {
int queueSize = threadPoolTaskExecutor.getThreadPoolExecutor().getQueue().size();
log.info("当前阻塞队列任务数:{}" , queueSize);
log.info("当前活动线程数:{}" ,threadPoolTaskExecutor.getActiveCount());
long completedTaskCount = threadPoolTaskExecutor.getThreadPoolExecutor().getCompletedTaskCount();
log.info("线程池完成任务数:{}" ,completedTaskCount);
//当所有任务都完成后,那么completedTaskCount=taskCount
long taskCount = threadPoolTaskExecutor.getThreadPoolExecutor().getTaskCount();
log.info("总线池总任务数:{}" ,taskCount);
try {
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
//获取线程池内最新的线程数量
log.info("当前线池内的程数为:{}",threadPoolTaskExecutor.getPoolSize());
log.info("############################");
}
}
效果:
4.结论:
1.当并发任务数量超过核心线程数+任务队列,且小于最大线程数+任务队列时,线程池会主动创建新的线程;
2.在空闲时间内,超过核心线程的线程对象会被淘汰;
3.当并发任务数超过最大线程数+任务队列长度时,触发线程的拒绝策略;