大家好,这里是淇妙小屋,一个分享技术,分享生存的博主
以下是我的主页,各个主页同步更新优质博客,创作不易,还请大家点波关注
掘金主页
后续会公布更多MySQL,Redis,并发,JVM,分布式等面试热点常识,以及Java学习路线,面试重点,职业规划,面经等相干博客
转载请表明出处!

1. Executor框架介绍

1.1 工作的两级调度模型

应用程序通过Executor框架管制下层的调度

上层的调度有OS内核管制,不受利用程序控制

1.2 Executor架构构造

Executor架构分为三个局部

  • 工作
  • 工作的返回后果
  • 执行工作

2. Future接口

用于管制工作的执行,取得异步工作的执行状态,执行后果

向线程池提交Callable工作,线程池会返回一个Future对象供咱们查看异步工作的执行状态,执行后果

Future<String>future=executor.submit(new Callable<String>(){});//高低等价RunnableFuture<String>future=new FutureTask<String>(异步工作);executor.execute(future);

3. ThreadPoolExecutor详解

3.1 线程池的状态

  • 线程池有五种运行状态

    • RUNNING(运行)——能够接管新的工作并执行
    • SHUTDOWN(敞开)——不再接管新的工作,然而仍会解决曾经提交的工作(包含线程正在执行的和处于阻塞队列中的)
    • STOP(进行)——不再接管新的工作,不会解决阻塞队列中的额工作,并且会中断正在执行的工作
    • TIDYING(整顿)——所有的工作都曾经终止,将线程池状态转换为TIDYING的线程会调用terminated()
    • TERMINATED(终止)——曾经执行结束terminated(),线程池终止
  • 线程池保护了一个AtomicInteger变量来示意线程池所处的状态(该变量还能够示意线程池中的线程数)
  • 线程池状态变动

3.2 手动创立线程池

    public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue,                              ThreadFactory threadFactory,                              RejectedExecutionHandler handler) {        //略        this.corePoolSize = corePoolSize;        this.maximumPoolSize = maximumPoolSize;        this.workQueue = workQueue;        this.keepAliveTime = unit.toNanos(keepAliveTime);        this.threadFactory = threadFactory;        this.handler = handler;    }

参数介绍

  • corePoolSize:初始化ThreadPoolExecutor中的corePoolSize,指定外围线程的数量
  • maximumPoolSize:初始化ThreadPoolExecutor中的maximunPoolSize,代表线程池中容许的最大线程数
  • keepAliveTime:初始化ThreadPoolExecutor中的keepAliveTime,闲暇线程容许存活的最大工夫
  • unit:keepAliveTime的单位
  • workQueue:初始化ThreadPoolExecutor中的workQueue,工作队列
  • threadFactory:初始化ThreadPoolExecutor中的threadFactory,线程工厂,用于创立线程
  • handler:初始化ThreadPoolExecutor中的handler,回绝策略

    ThreadPoolExecutor内置4种回绝策略

    • AbortPolicy:间接抛出异样。
    • CallerRunsPolicy:由提交工作的线程解决工作
    • DiscardOldestPolicy:抛弃队列中最老的工作,从新提交这个被回绝的工作
    • DiscardPolicy:不解决,抛弃掉。

3.3 execute()执行过程

%E8%BF%87%E7%A8%8B.png)过程](../p/execute()过程.png)

3.4 Woker和addWork()逻辑

  • 线程池中,线程被封装为Worker
  • thread:由线程池中的threadFactory创立
  • firstTask:创立Worker的时,能够指定firstTask,如果firstTask不为null,那么线程优先执行firstTask

  • 创立外围线程失败状况

    • 线程池的状态是STOP,TIDYING,TERMINATED
    • 线程池的状态是SHUTDOWN,并且传入了工作不是null(SHUTDOWN状态下,线程池不再承受新的工作)
    • 以后线程数>=容许的外围线程数
  • 创立非核心线程失败状况

    • 线程池的状态是STOP,TIDYING,TERMINATED
    • 线程池的状态是SHUTDOWN,并且传入了工作不是null(SHUTDOWN状态下,线程池不再承受新的工作)
    • 以后线程数>=容许的最大线程数

3.5 Worker工作过程

  • Woker执行的工作有2个起源

    1. Woker创立时指定的firstTask
    2. 从阻塞队列获取
  • Woker分为阻塞 外围线程非核心线程——依据 以后线程数目是否<= corePoolSize来判断(所以对于同一个线程,某一时刻能够是外围线程,另一时刻能够是非核心线程)
  • 默认状况下,外围线程会在阻塞队列永恒阻塞获取,并且不会销毁,非核心线程只会在阻塞队列阻塞获取keepAliveTime的工夫,超过了会进行销毁

    然而如果线程池设置了 allowCoreThreadTimeOut,那么外围线程的待遇就跟非核心线程一样了

3.6 敞开线程池

3.6.0 尝试销毁线程池——tryTerminate()

尝试将线程池的状态更改为 TERMINATED,只有以下2种状况能力胜利

  • 线程池状态为 SHUTDOWN,线程池中没有线程,并且阻塞队列为空
  • 线程池为 STOP,线程池中没有线程

3.6.1 迟缓敞开线程池——shutdown()

  1. 确保调用者有权限拜访线程池中的线程
  2. 将线程池的状态批改为 SHUTDOWN
  3. 对线程池中的所有线程调用其interrupt()传递中断信号
  4. 调用tryTerminal()尝试销毁线程池
第4步大部分状况都不会胜利线程池状态变为SHUTDOWN后,线程池不会再承受新的工作,但曾经承受的工作仍会继续执行,当所有工作执行完后,线程检测到线程池状态为SHUTDOWN并且工作队列空了,那么线程会执行退出操作——在退出操作中,每个线程都会执行一次 tryTerminal(),最初一个退出的线程能够胜利销毁线程池

3.6.2 疾速敞开线程池——shutdownNow()

  1. 确保调用者有权限拜访线程池中的线程
  2. 将线程池的状态批改为 STOP
  3. 对线程池中的所有线程调用其interrupt()传递中断信号
  4. 移出工作队列中所有未执行的工作
  5. 调用tryTerminal()尝试销毁线程池
shutdownNow()移除工作队列中所有未执行的工作,从而实现疾速敞开线程池

3.7 预热办法

3.7.1 prestartCoreThread

在线程池中事后创立一个线程

3.7.2 prestartAllCoreThreads

在线程池中创立所有的外围线程

3.8 面试题

3.8.1 如何了解keepAliveTime

如果线程池中的线程数目>corePoolSize,那么多余的线程一旦闲暇超过keepAliveTime,就会销毁线程,直到线程数目==corePoolSize

3.8.2 为什么工作先放在工作队列中,而不是间接把线程数目拉到最大

我的集体了解

我认为线程池的本意是让外围数量的线程工作着,工作队列起到一个缓冲的作用,最大线程数目这个参数更像是无奈之举,在工作十分多的状况下做最初的致力,去新建线程来帮忙解决工作

原生的线程池偏差于 CPU密集型,工作过多时不是创立更多的线程,而是先缓存工作,让外围线程去解决

而像Tomcat这种业务场景,是 IO密集型,原生的线程池并不适合,须要定制(Tomcat的线程池就是定制的)

4. ScheduledThreadPoolExecutor

  • 继承自ThreadPoolExecutor,用于在给定的提早后执行工作or执行定时工作
  • 工作队列默认是用 DelayWorkQueue
  • 提交工作后,工作封装为ScheduledFutureTask后,间接进入工作队列,而后由线程从工作队列工作队列中获取ScheduledFutureTask执行

4.1 工作类图

4.2 工作提交

ScheduledThreadPoolExecutor中

schedule()scheduleAtFixedRate()scheduleWithFixedDelay()submit(),execute()的逻辑基本相同

接下来以schedule()为例进行解说

.png)

ScheduledThreadPool中,工作封装为ScheduledFutureTask后,间接进入工作队列,而后由线程从工作队列中获取ScheduledFutureTask执行

4.3 工作的执行

  • DelayWorkQueue和ScheduledFutureTask的构造

  • 工作工作执行的步骤

    1. 线程从DelayWorkQueue中获取超时的ScheduledFutureTask

      (从queue中获取queue[0],如果没有会阻塞期待,而后查看工作是否超时,如果工作没超时,会阻塞直到工作超时)

    2. 线程执行工作
    3. 线程批改ScheduledFutureTask的time为下一次执行的工夫
    4. 线程将ScheduledFutureTask从新放回DelayWorkQueue

5. FutureTask

  1. FutureTask能够由调用线程间接执行(FutureTask.run())(这种形式不会创立新的线程),也能够提交给线程池执行
  2. FutureTask跟Future一样,能够管制工作的执行状态,取得工作的执行后果

5.1 FutureTask状态迁徙图