Java线程池实现类ThreadPoolExecutor源码分析

线程池实现类ThreadPoolExecutor是在java.util.concurrent下的,从JDK1.5开始支持线程池实现类ThreadPoolExecutor.

该类有四个构造函数(不含无参构造函数),分别为:

// 常用的是这种,使用默认的线程工厂和拒绝策略

1、ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue)

// 用户自定义线程工厂

2、ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory)

// 用户自定义线程池饱和/执行拒绝策略

3、ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,

RejectedExecutionHandler handler)

// 用户自定义线程工厂和线程池饱和/执行拒绝策略

4、ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)

其中:1、2、3构造函数均调用this(args...)构造函数,即构造函数4。

Executors类提供了默认的ThreadFactory(线程工厂),即Executors.defaultThreadFactory(),

ThreadPoolExecutor提供了默认的线程池饱和和执行拒绝策略,private static final RejectedExecutionHandler defaultHandler =new AbortPolicy();

线程池实现类的构造函数参数:

corePoolSize(线程池核心线程大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,当任务数大于核心线程数的时候就不会再创建。在这里要注意一点,线程池刚创建的时候,其中并没有创建任何线程,而是等任务来才去创建线程,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法 ,这样才会预先创建好corePoolSize个线程或者一个线程

maximumPoolSize(线程池最大线程数):线程池允许创建的最大线程数,如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是,如果使用了无界队列,此参数就没有意义了

keepAliveTime(线程活动保持时间):此参数默认在线程数大于corePoolSize的情况下才会起作用, 当线程(默认是针对非核心线程)的空闲时间达到keepAliveTime的时候就会终止,直至线程数目小于corePoolSize。不过如果调用allowCoreThreadTimeOut方法,则当线程数目小于corePoolSize的时候也会起作用.

unit(keelAliveTime的时间单位):keelAliveTime的时间单位,一共有7种,在这里就不列举了。

workQueue(阻塞队列):阻塞队列,用来存储等待执行的任务,这个参数也是非常重要的,在这里简单介绍一下几个阻塞队列。

    1)ArrayBlockingQueue:这是一个基于数组结构的有界阻塞队列,此队列按照FIFO的原则对元素进行排序。

    2)LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按照FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()就是使用了这个队列。

    3)SynchronousQueue:一个不存储元素的阻塞队列每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool()就使用了这个队列。

    4)PriorityBlockingQueue一个具有优先级的无阻塞队列

handler(饱和策略);当线程池和队列都满了,说明线程池已经处于饱和状态了,那么必须采取一种策略来处理还在提交过来的新任务。这个饱和策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。共有四种饱和策略提供,当然我们也可以选择自己实现饱和策略。

AbortPolicy:直接丢弃并且抛出RejectedExecutionException异常,这种是默认的饱和策略

CallerRunsPolicy:只用调用者所在线程来运行任务,如果线程池处于Running状态,直接在提交任务的线程中执行任务;如果线程池不处于Running状态,直接丢弃任务。

DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务

DiscardPolicy:丢弃任务并且不抛出异常,如果线程池处于Running状态,从阻塞队列中移除头部任务并重新调用execute方法

ThreadPoolExecutor类中的几个关键属性及方法源码解析:

查看ThreadPoolExecutor源码,可以发现,有以下属性:

//ctl是线程池主要的控制状态,是一个复合类型的变量,其中包括了两个概念。

// ctl的高3位代表线程池的状态,低29位代表workerCount

// workerCount:表示有效的线程数目(低29位)

// runState:线程池里线程的运行状态(高3位)

// 线程池的控制状态主要分为:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATE

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 

// COUNT_BITS用来表示线程池数量的位数,Integer.SIZE=32,因此COUNT_BITS=29,减3是为线程池运行状态预留3位bit位。

private static final int COUNT_BITS = Integer.SIZE - 3; 

// 线程池最大线程数量,线程池最大线程数量为2^29-1个

private static final int CAPACITY = (1 << COUNT_BITS) - 1; 

// runState is stored in the high-order bits,线程池运行状态,存储在ctl变量的高位(高3位)

// 高3位为111,则是在RUNNING状态,

private static final int RUNNING = -1 << COUNT_BITS; 

// 高3位位000,则是在SHUTDOWN状态

private static final int SHUTDOWN = 0 << COUNT_BITS;      // 0 

// 高3位为001,则是在STOP状态

private static final int STOP = 1 << COUNT_BITS; 

// 高3位为010,则是在TIDYING状态

private static final int TIDYING = 2 << COUNT_BITS; 

// 高3位为110,则是在TERMINATED状态

private static final int TERMINATED = 3 << COUNT_BITS; 

// Packing and unpacking ctl 

// 获取当前运行状态,通过对CAPACITY取反,然后和传进来的参数进行与运算

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; }  

// 阻塞队列

private final BlockingQueue workQueue;

// 可重入锁,访问workers集合、访问largestPoolSize等线程池的统计型变量、执行shutdown方法、执行shutdownNow方法都需要获取该锁

private final ReentrantLock mainLock = new ReentrantLock();

// 线程池内的有效线程

private final HashSet workers = new HashSet();

//和mainLock绑定的Condition对象,执行awaitTermination方法和tryTerminate方法时使用该Condition对象

private final Condition termination = mainLock.newCondition();

// 线程工厂

private volatile ThreadFactory threadFactory;

// 线程池达到饱和后的拒绝策略

private volatile RejectedExecutionHandler handler;

线程池的控制(运行)状态

线程池的控制状态主要分为:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATE

每种控制状态下,对新创建的线程(任务)和已在阻塞队列的线程(任务)的策略都有不同,

1)RUNNING状态:线程池可以接受新任务并且处理已经在阻塞队列的任务。

2)SHUTDOWN状态: 线程池不接受新任务,但是处理已经在阻塞队列的任务。

3)STOP状态: 线程池不接受新任务,也不处理阻塞队列里的任务并且会中断正在处理的任务。

4)TIDYING状态:线程池所有任务都被中止,workerCount(有效线程数量)是0,线程状态转化为TIDYING并且调用terminated()钩子方法。

5)TERMINATE状态:terminated()钩子方法已经完成。

线程池执行线程(调用execute(Runnable command)方法)流程:

1、首先判断任务是否为空,空则抛出空指针异常(if command==null throw new NullPointerException();)

2、command不为空则获取线程池控制状态ctl值(包括:运行状态和有效线程数),判断有效线程数是否小于corePoolSize,小于添加到worker集合当中执行,如成功,则返回(执行线程command任务),失败的话再接着获取线程池控制状态,因为只有状态变了才会失败,所以重新获取。

3、判断线程池是否处于运行状态是的话则添加command到阻塞队列,加入时也会再次获取状态并且检测

​ 状态是否不处于运行状态,不处于的话则将command从阻塞队列移除,并且拒绝任务

4、如果线程池里没有了线程(workerCountOf 方法返回值为0),则创建新的线程去执行获取阻塞队列的任务执行。

5、如果以上都没执行成功,则需要开启最大线程池里的线程来执行任务,失败的话就丢弃。

线程池执行线程的流程图例,(借用作者:KingJack文章配图):


线程池执行线程流程

你可能感兴趣的:(Java线程池实现类ThreadPoolExecutor源码分析)