线程池原理解析一
讲解线程池原理之前,首先了解一下什么是线程池:
线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。线程池中的每个线程都有被分配一个任务,一旦任务已经完成了,线程回到线程池中并等待下一次分配任务。
至于为什么要使用线程池?
对于操作系统来说,频繁的创建销毁线程会消耗大量的资源。所以可以创建线程池来统一管理这些运行的线程。线程用于在需要执行大量异步任务的情况下,使用线程池减少了每个任务运行的负载。
了解完线程池后,接下来了解它是如何使用的:
JDK1.5提供了Executor框架,开发者可以使用线程池工厂类Executors根据不同的需求创建各种不同形式的线程池。常用的有以下几种线程池:
1、newCachedThreadPool:

创建一个阻塞队列为SynchronousQueue的线程池。当调用execute方法时,将重用空闲的工作线程。如果没有工作线程可利用,则将创建新的工作线程并添加到线程池。空闲线程超过60s将会被回收。
2、newFixedThreadPool:

创建一个固定数量的工作线程的线程池。如果所有的工作线程在执行任务,新提交的任务将会在任务队列中等待,直到有工作线程可利用。一旦线程空闲下来就会被回收。
3、newSingleThreadExecutor:

创建只有一个工作线程的executor。(工作线程在线程池关闭之前,如果执行任务失败,则创建一个新的工作线程来替代旧的工作线程)。
其实大多数线程池的本质都是初始化一个ThreadPoolExecutor对象。

corePoolSize:线程池核心线程数。
keepAliveTime:空闲线程允许的最大空闲时间。
defaultThreadFactory:用于创建线程的工厂方法。
maximumPoolSize: 线程池可以容纳的最大线程数。
workQueue:用于存放任务的阻塞队列,提交的任务需要实现Runnable接口。JDK提供了如下几种阻塞队列:
1、ArrayBlockingQueue:可以阻止资源的浪费,但在不可估量负载的情况下,可能出现吞吐量下降的情况。
2、LinkedBlockingQueue:当任务提交的速度大于线程处理任务的速度时,可能出现任务无限制提交的情况。
3、SynchronousQueue:每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。
4、PriorityBlockingQueue:插入的元素必须可比较。
defaultHandler:用于处理被拒绝的任务。当线程数到达最大线程数并且阻塞队列达到饱和,execute方法会调用RejectedExecutionHandler的rejectedExecution处理被拒绝的任务。JDK提供如下几种拒绝策略:
1、ThreadPoolExecutor.AbortPolicy:默认情况下执行,直接抛出RejectedExecutionException运行时异常。
2、CallerRunsPolicy:线程调用execute方法执行任务。这种策略提供了一个反馈机制减慢新任务的提交速度。
3、DiscardPolicy:直接丢弃新提交的任务 。
4、DiscardOldestPolicy:如果executor没有关闭,队列头的任务将会被丢弃,然后executor重新尝试执行任务(如果失败,则重复这一过程)。
5、我们也可以自己定义RejectedExecutionHandler以适应特殊环境的需求。
然后来了解一下线程池状态。

在ThreadPoolExecutor类中有个ctl变量用于显示线程池状态。其中AtomicInteger是一个提供原子操作的Integer类,保证多线程情况下ctl的变化是线程安全的。ctl是个神奇的变量,它的高3位用于显示线程池状态,低29位用于显示线程池中的线程数。
1、RUNNING:接受新的任务,处理队列任务。
2、SHUTDOWN:不再接受新的任务,处理队列任务。
3、STOP:不再接受新任务,不处理队列任务,中断正在执行的任务线程。
4、TIDYING:所有的任务已经结束,任务线程为0,线程转换到TIDYING状态。
5、TERMINATED:线程池已将结束,即terminated()方法执行完。
随着时间的推移,线程池状态之间也是在发生着转化,以下是几种状态转化的条件。
1、RUNNING -> SHUTDOWN(调用shudown方法)
2、RUNNING或者SHUTDOWN -> STOP(调用shutdown方法)
3、SHUTDOWN -> TIDYING (当线程池和队列都为空)
4、STOP -> TIDYING (当线程池为空)
5、TIDYING -> TERMINATED (terminated方法执行完)
目前先对线程池做个大致的了解,对于如何调用addWorker,runWorker等方法留到下篇再解释。
本篇文章主要是看到占小狼大神的《深入分析java线程池的实现原理》,然后想有样学样的总结一下线程池原理,同时看了一下线程池几个核心类的源码并且翻译了一下注释,有兴趣的小伙伴可以去看看注释。对于这篇总结,如果哪里写的不对,谢谢批评指正。
参考:https://www.jianshu.com/p/87bff5cc8d8c