listenfwind

1.ThreadPoolExcuter原理说明
  首先我们要知道为什么要使用ThreadPoolExcuter,具体可以看看文档中的说明:
  线程池可以解决两个不同问题:由于减少了每个任务的调用开销,在执行大量的异步任务时,它通常能够提供更好的性能,并且还可以提供绑定和管理资源(包括执行集合任务时使用的线程)的方法。每个 ThreadPoolExecutor还维护着一些基本的统计数据,如完成的任务数。
  线程池做的其实可以看得很简单,其实就是把你提交的任务(task)进行调度管理运行,但这个调度的过程以及其中的状态控制是比较复杂的。

2.初始化参数介绍
可以直接看最完整的ThreadPoolExcuter的初始化函数:

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

逐个介绍如下:
corePoolSize:核心线程数,在ThreadPoolExcutor中有一个与它相关的配置:allowCoreThreadTimeOut(默认为false),当allowCoreThreadTimeOut为false时,核心线程会一直存活,哪怕是一直空闲着。而当allowCoreThreadTimeOut为true时核心线程空闲时间超过keepAliveTime时会被回收。

maximumPoolSize:最大线程数,线程池能容纳的最大线程数,当线程池中的线程达到最大时,此时添加任务将会采用拒绝策略,默认的拒绝策略是抛出一个运行时错误(RejectedExecutionException)。值得一提的是,当初始化时用的工作队列为LinkedBlockingDeque时,这个值将无效。

keepAliveTime:存活时间,当非核心空闲超过这个时间将被回收,同时空闲核心线程是否回收受allowCoreThreadTimeOut影响。

unit:keepAliveTime的单位。

workQueue:任务队列,常用有三种队列,即SynchronousQueue,LinkedBlockingDeque(*队列),ArrayBlockingQueue(有界队列)。

threadFactory:线程工厂,ThreadFactory是一个接口,用来创建worker。通过线程工厂可以对线程的一些属性进行定制。默认直接新建线程。

RejectedExecutionHandler:也是一个接口,只有一个方法,当线程池中的资源已经全部使用,添加新线程被拒绝时,会调用RejectedExecutionHandler的rejectedExecution法。
默认是抛出一个运行时异常。

  这里需要做一个额外说明,在ThreadPoolExcuter中,worker和task是有区别的,task是用户提交的任务,而worker则是用来执行task的线程。在初始化参数中,corePoolSize和maximumPoolSize都是针对worker的,而workQueue是用来存放task的。

3.ctl介绍以及运行状态说明
  首先需要介绍线程池有五种运行状态:
RUNNING(状态值-1): 接收新任务并处理队列中的任务
SHUTDOWN(状态值0): 不接收新任务但会处理队列中的任务。
STOP(状态值1): 不接收新任务,不处理队列中的任务,并中断正在处理的任务
TIDYING(状态值2): 所有任务已终止,workerCount为0,处于TIDYING状态的线程将调用钩子方法terminated()。
TERMINATED(状态值3): terminated()方法完成。

  然后我们可以看看ThreadPoolExcuter中的ctl这个变量。
  ctl是ThreadPoolExcuter中比较有意思的一个实现,它是一个AtomicInteger,这里不对AtomicInteger多做讨论,只要知道可以把它看成有原子性的Integer就够了,其实它具有原子性的原理是使用了CAS的技术,这是一种乐观锁的具体实现。
  ThreadPoolExcuter是将两个内部值打包成一个值,即将workerCount和runState(运行状态)这两个值打包在一个ctl中,因为runState有5个值,需要3位,所以有3位表示
runState,而其他29位表示为workerCount。
  而运行时要获取其他数据时,只需要对ctl进行拆包即可。具体这部分代码如下:

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    // Packing and unpacking ctl  
  //拆包ctl,分别获取runState和WorkerCount
private static int runStateOf(int c) { return c & ~CAPACITY; } private static int workerCountOf(int c) { return c & CAPACITY; }
  //打包操作
private static int ctlOf(int rs, int wc) { return rs | wc; }

 

总结:本篇初步介绍了ThreadPoolExcuter的基本原理,解决了什么问题。而后说明了ThreadPoolExcuter中的初始化参数,对其中的各个参数做初步介绍。再之后介绍ctl变量的作用。

分类:

技术点:

相关文章: