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("############################");
        }
    }

效果:

java中线程池非核心线程的过期策略 线程池核心线程数为0_java

并发任务数量超过最大线程数+任务队列时的场景:

定义循环线程数量: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("############################");
        }
    }

效果:

java中线程池非核心线程的过期策略 线程池核心线程数为0_任务队列_02

4.结论:

1.当并发任务数量超过核心线程数+任务队列,且小于最大线程数+任务队列时,线程池会主动创建新的线程;
2.在空闲时间内,超过核心线程的线程对象会被淘汰;

3.当并发任务数超过最大线程数+任务队列长度时,触发线程的拒绝策略;