$1. 概述

线程池产生背景:高并发我的项目中常常须要同时启用大量线程,因而须要创立大量线程,而频繁的创立新线程和杀死旧线程将会大大拖慢CPU的性能,因而利用线程池一次性初始化若干线程,应用时从池中弹出线程,执行完线程池再予以回收

线程池的工作流程:次要是管制运行的线程数量,解决过程中将工作放入队列,而后在线程创立后启动这些工作,如果线程数量超过了最大数量,超出数量的线程排队等待,等其余线程执行结束,再从队列中取出工作执行

线程池的次要特点:实现线程复用、管制最大并发数;可能系统地治理线程

线程池的劣势

  • 升高资源耗费,通过反复利用已创立的线程升高线程频繁创立和销毁的损耗
  • 进步响应速度,当工作达到时,工作能够不须要期待线程创立就能立刻执行
  • 进步线程的可管理性,线程池能够对线程进行对立的调配调度、调优以及监控

$2. 线程池的实现形式

2.1 通过Executors工具类

  • Executors.newFixedThreadPool(int nThreads):固定线程数量线程池
// 源码  public static ExecutorService newFixedThreadPool(int nThreads) {   return new ThreadPoolExecutor(nThreads, nThreads,                                 0L, TimeUnit.MILLISECONDS,                                 new LinkedBlockingQueue<Runnable\>());  }
  • Executors.newSingleThreadExecutor():单线程线程池
// 源码  public static ExecutorService newSingleThreadExecutor() {   return new FinalizableDelegatedExecutorService   (new ThreadPoolExecutor(1, 1,                           0L, TimeUnit.MILLISECONDS,                           new LinkedBlockingQueue<Runnable\>()));  }
  • Executors.newCachedThreadExecutor():可扩容线程池,实践上反对Integer.MAX_VALUE条线程
// 源码public static ExecutorService newCachedThreadPool() {   return new ThreadPoolExecutor(0, Integer.MAX\_VALUE,                                 60L, TimeUnit.SECONDS,                                 new SynchronousQueue<Runnable\>());  }

本质上,这三种线程池尽管各有特点,但底层实现的都是ThreadPoolExecutor类,也即最正统的线程池,线程池本池☺

  • 理论应用
// 应用案例import java.util.concurrent.Executors;public class NewFixedThreadPoolDemo {    public static void main(String[] args) {        ExecutorService threadPool = Executors.newFixedThreadPool(5);        // ExecutorService threadPool = Executors.newSingleThreadExecutor();        // ExecutorService threadPool = Executors.newCachedThreadExecutor();                        for(int i=0; i<10; i++){            threadPool.execute(()-> System.out.println(Thread.currentThread().getName() + "\t 办理业务"));        }                threadPool.shutdown(); // 敞开资源!!    }}

2.2 通过ThreadPoolExecutor[线程池本池!用它!用它! 用它!]

WHY?(谁说的不能用Executors?)

  • 马云曰:我说的!(阿里编程标准明确示意不倡议外部应用Executors工具类实现线程池)
  • fixThreadPool和SingleThreadPool设置的工作申请队列为Integer.MAX_VALUE,可能会沉积大量的申请,从而导致OOM
  • CachedThreadPool和ScheduledThreadPool容许创立的线程数量为Integer.MAX_VALUE, 可能会创立大量线程,从而导致OOM
// 代码实现import java.util.concurrent.*;public class ThreadPoolExecutorDemo {    public static void main(String[] args) {        ExecutorService threadPool = new ThreadPoolExecutor(2,                5,                2L,                TimeUnit.SECONDS,                new LinkedBlockingDeque<>(3),                Executors.defaultThreadFactory(),                new ThreadPoolExecutor.DiscardPolicy());        for(int i=1; i<=10; i++){            final int temp = i;            threadPool.execute(()-> System.out.println(Thread.currentThread().getName() + "\t办理业务" + temp));        }        threadPool.shutdown();    }}

参数解析

  • corePoolSize:线程池中的常驻外围线程数
  • maximumPoolSize:线程池中可能包容同时执行的最大线程数,该值必须大于等于1
  • keepAliveTime:多余的闲暇线程的存货工夫,以后池中线程数量大于corePoolSize时,当闲暇线程的闲暇工夫达到keepAliveTime,多余的线程会被销毁直到剩下corePoolSize个
  • unit:keepAliveTime的单位
  • workQueue:工作队列,被提交但尚未被执行的工作 -- 设想银行的等待区
  • threadFactory:示意生成线程池中工作线程的线程工厂,用于创立线程,个别应用默认即可
  • handler:回绝策略,示意当线程池和队列都满了,线程池采取何种策略来拒绝请求执行的Runnable工作

$3. 线程池工作原理

talk is cheap, show me the diagram

线程池个别工夫都只会开启外围线程数量的线程,而当工作队列满了之后,线程池会启用非核心线程池区(这里形容只是逻辑分区,实际上线程之间都是等同位置),创立线程并执行工作;而当工作队列中工作变少,有些线程开始闲置,闲置工夫达到设置的keepAliveTime后,线程池会登记回收这些闲置线程,直到线程数量复原外围线程数

  • 一个线程池可能并发解决的最大工作数 = maximumPoolSize + workQueue设置的长度

$4. 回绝策略(渣男[女]必备)

共有四种回绝策略,以2.2中的代码为准,即设置外围线程数为2、最大线程数为5、工作队列容量为3、工作数为10;见四种策略别离的运行后果如下

  • AbortPolicy:当无奈执行工作时,间接抛出RejectedExecutionException中断程序

  • CallerRunsPolicy:调用者运行机制,该策略不会摈弃工作,也不会抛出异样,而是将无奈执行的工作返还给调用者执行,这里因为是main线程调用的,因而返还给main线程执行工作。如下图,工作9被返还给main线程执行了,这过程的工夫内线程池可能曾经执行完工作1了,工作队列从而空了一个地位,工作10便间接进入工作队列没有返还给main线程。在保障工作完全性的状况下,该策略是惟一策略

  • DiscardOldestPolicy:摈弃队列中期待最久的工作,而后把当前任务退出到队列中尝试再次提交当前任务

  • DiscardPolicy:该策略会抛弃无奈解决的工作,不做任何解决也不抛异样,如果容许工作失落,这是最好的一种策略

REFERENCE

尚硅谷周阳老师JUC课程:https://www.bilibili.com/vide...