线程池实现类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文章配图):