JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor

concurrent包类图如下,总共分为四大部分 线程池 ThreadPoolExecutor,Future,BlockingQueue,ConcurrentMap

JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor_第1张图片

先看下ExecutorService接口
作用:
1. 执行任务Runnable(execute方法)
2. 提交任务Runnable、Callable(submit方法)
3. 一次提交多个任务(invoke方法)
4. 关闭提交任务接口(shutdown方法)
5. 关闭、停止状态监测(isShutdown、isTerminated、awaitTermination方法)
JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor_第2张图片

JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor_第3张图片

AbstractExecutorService
AbstractExecutorService实现了ExecutorService接口作用的第2和第3点(提交任务)
submit()实现:创建FutureTask,并交给execute()方法执行
invokeAny()实现:使用CompletionService提交任务,并获取结果,如果超时还未获取到结果,则抛出ExecutionException异常
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

一、前言

  JUC这部分还有线程池这一块没有分析,需要抓紧时间分析,下面开始ThreadPoolExecutor,其是线程池的基础,分析完了这个类会简化之后的分析,线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行任务集时使用的线程)的方法。下面开始分析。

二、ThreadPoolExecutor数据结构

   在ThreadPoolExecutor的内部,主要由BlockingQueue和AbstractQueuedSynchronizer对其提供支持,BlockingQueue接口有多种数据结构的实现,如LinkedBlockingQueue、ArrayBlockingQueue等,而AbstractQueuedSynchronizer在之前有过详细的分析,有兴趣的读者可以参考。

JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor_第4张图片

 说明:可以看到Worker继承了AQS抽象类并且实现了Runnable接口,其是ThreadPoolExecutor的核心内部类。而对于AbortPolicy,用于被拒绝任务的处理程序,它将抛出 RejectedExecutionException、CallerRunsPolicy,用于被拒绝任务的处理程序,它直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务、DiscardPolicy,用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务、DiscardOldestPolicy,用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试 execute;如果执行程序已关闭,则会丢弃该任务。这些都是拒绝任务提交时的所采用的不同策略。

JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor_第5张图片

JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor_第6张图片

JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor_第7张图片

  3.5 核心函数分析

  1. execute函数  

JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor_第8张图片

JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor_第9张图片

 

JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor_第10张图片

run方法执行的时候,也是要加重入锁的

JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor_第11张图片

说明:此函数中会实际执行给定任务(即调用用户重写的run方法),并且当给定任务完成后,会继续从阻塞队列中取任务,直到阻塞队列为空(即任务全部完成)。在执行给定任务时,会调用钩子函数,利用钩子函数可以完成用户自定义的一些逻辑。在runWorker中会调用到getTask函数和processWorkerExit钩子函数,其中,getTask函数源码如下  

JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor_第12张图片

 说明:此函数会按过去执行已提交任务的顺序发起一个有序的关闭,但是不接受新任务。首先会检查是否具有shutdown的权限,然后设置线程池的控制状态为SHUTDOWN,之后中断空闲的worker,最后尝试终止线程池。
尝试终止线程池tryTerminate的源码如下

说明:如果线程池的状态为SHUTDOWN并且线程池和阻塞队列都为空或者状态为STOP并且线程池为空,则将线程池控制状态转化为TERMINATED;否则,将中断一个空闲的worker,其中,interruptIdleWorkers的源码如下 

四、示例

  通过上面的分析,对于一些重要的函数有了一个整体的认识,下面通过一个示例,看看这些函数之间是如何串联起来的,并且分析分析ThreadPoolExecutor的工作机制。

JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor_第13张图片

 说明:在调用了es.submit(mr1)后,最终线程池中会新建一个worker,并且此时workQueue阻塞队列为空(没有元素),并且值得注意的是,在runWorker函数中,有一个while循环,当某个任务完成后,会从workQueue阻塞队列中取下一个任务。

  ② 执行es.submit(mr2),其主要的函数调用与执行es.submit(mr1)相同,但是此时的线程池状态有所不同,其状态如下

JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor_第14张图片

说明:此时,由于线程池的worker的数量已经达到了corePoolSize大小,所以,此时会将mr3放入到workQueue阻塞队列中,此时,线程池还是只有两个worker,并且阻塞队列已经存在一个mr3元素。

  ④ mr2定义的逻辑运行完成,则会从workQueue中取下一个任务(mr3)。主要的函数调用如下(从runWorker开始)

关于ThreadPoolExecutor还有如下几点需要注意的

  ① corePoolSize,表示核心大小,如果运行的线程少于 corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。

  ② maxPoolSzie,表示阻塞队列的大小,如果运行的线程多于 corePoolSize 而少于 maximumPoolSize,则仅当阻塞队列满时才创建新线程。如果设置的 corePoolSize 和 maximumPoolSize 相同,则创建了固定大小的线程池(如本例的FixThreadPool)。如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务。

  ③ largestPoolSize,表示曾经同时存在在线程池的worker的大小,为workers集合(维护worker)的大小。

  ④ 关于shutdown函数和shutdownNow函数的区别,shutdown会设置线程池的运行状态为SHUTDOWN,并且中断所有空闲的worker,由于worker运行时会进行相应的检查,所以之后会退出线程池,并且其会继续运行之前提交到阻塞队列中的任务,不再接受新任务。而shutdownNow则会设置线程池的运行状态为STOP,并且中断所有的线程(包括空闲和正在运行的线程),在阻塞队列中的任务将不会被运行,并且会将其转化为List返回给调用者,也不再接受新任务,其不会停止用户任务(只是发出了中断信号),若需要停止,需要用户自定义停止逻辑。

 

 

 

 

 

你可能感兴趣的:(JDK 源码复习 concurrent 包 01 线程池ThreadPoolExecutor)