启动线程数 = 【任务执行时间/(任务执行时间-IO等待时间)】*CPU内核数
    线程池是一种基于池化思想管理线程的工具,可以用来避免频繁的创建和释放线程带来的开销以及在高并发场景下线程数量膨胀导致的过度调度的问题
    其他池化思想:内存池,连接池,对象池
    使用线程池的好处
    1.降低资源消耗
    2.提高响应速度
    3.易于管理线程
    4.提供更强大的api,比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。

    线程池的底层原理:
    1.线程池如何维护自身状态
    2.线程池如何管理任务
    3.线程池如何管理线程

    线程池中有一个AtomicInteger变量ctl维护了两个值,线程池状态和线程数量,前三位表示线程池状态,后29位表示线程数量
    image.png
    image.png
    ThreadPoolExcuter的excute方法通过检查线程池的运行状态,线程数和运行策略来决定接下来的流程,先检查线程的状态,如果是Running态才接受新的任务,如果线程池的大小小于核心线程数,那就创建一个新的线程执行,如果大于,那就检查阻塞队列是否满,没满就加入阻塞队列,如果阻塞队列满了并且小于最大线程数就创建一个新的线程执行任务,如果大于最大线程数,那就根据当前的拒绝策略来执行该任务,默认的方式是抛出异常
    执行任务有两种方式,一种是线程池创建线程执行,一种是线程去阻塞队列里面取,阻塞队列相当于一个缓冲池,一个元素往里面加任务另外一个往里面取任务,相当于生产者消费者模型
    image.png
    image.png
    线程池的四种拒绝策略
    1.抛出异常(默认,比较适合一些关键的任务,交给应用程序来实现)
    2.丢弃新来的任务(如果业务比较无关紧要就可以使用)
    3.丢弃队列开头的任务,执行新的任务(如果业务允许丢弃老任务就可以使用)
    4.由提交任务的线程取完成(这种情况需要所有任务都执行完毕)

    Worker线程继承了AQS来实现独占锁的功能,实现了Runnable接口并且有一个thread线程(工厂设计模式创建)变量和一个Runnable firsttask变量,如果不为空就直接执行,为null就去阻塞队列里面取任务,执行shutdown方法时会执行AQS的tryLock方法尝试获取锁来判断里面是否有空闲的线程,如果有就进行回收
    Worker的run方法不断getTask从阻塞队列中获取线程,如果获取到则执行任务

    线程池的使用场景:
    1.快速响应用户,比如说获取商品信息要获取商品的价格,图片,库存等信息,就可以使用线程池并行的去请求这些信息再返回结果,并且不设置阻塞队列去缓冲任务,并且提高核心线程数和最大线程数的大小
    2.如果是一些需要吞吐量的场景就设置阻塞队列,并且配置好大小,防止队列积压

    线程池使用不当的事故:
    1.没有预估好流量,最大线程数设置的太少抛出异常导致接口降级
    2.阻塞队列设置的太大导致队列积压导致下游服务超时
    线程池的配置可以存储在配置中心里面实现动态配置的线程池(线程池监听外部消息)

    动态化线程池提供如下功能:

    • 动态调参:支持线程池参数动态调整、界面化操作;包括修改线程池核心大小、最大核心大小、队列长度等;参数修改后及时生效。
    • 任务监控:支持应用粒度、线程池粒度、任务粒度的Transaction监控;可以看到线程池的任务执行情况、最大任务执行时间、平均任务执行时间、95/99线等。
    • 负载告警:线程池队列任务积压到一定值的时候会通过大象(美团内部通讯工具)告知应用开发负责人;当线程池负载数达到一定阈值的时候会通过大象告知应用开发负责人。
    • 操作监控:创建/修改和删除线程池都会通知到应用的开发负责人。
    • 操作日志:可以查看线程池参数的修改记录,谁在什么时候修改了线程池参数、修改前的参数值是什么。
    • 权限校验:只有应用开发负责人才能够修改应用的线程池参数。

    image.png
    jdk提供了set方法动态的对线程池参数进行调整
    image.png
    动态化的关键:线程池监听外部消息,线程池管理平台,监控最大运行时间,平均运行时间,队列大小,必要进行报警

    Excutor中的线程池:
    FixedThreadPool,核心线程数和最大线程数相等,执行长期任务性能好,但用的无界队列
    SingleThreadPool,里面只有一个worker线程,适合单任务的场景(比普通的多个阻塞队列),用的无界队列
    newCachedThreadPool,使用同步队列,核心线程数为0,最大线程数没有限制,对keepalivetime限时60s,适合执行很多短期异步的小程序或者负载较轻的服务器
    NewScheduledThreadPool执行定期任务
    其实这四种线程池是建议使用的
    image.png

    为什么要加个阻塞队列才加非核心线程这样设计?
    最佳实践
    1.不要使用Excutor静态工厂创建线程池,FixedThreadPool默认使用的LinkedBlockingQueue无界队列,可能会导致OOM问题