线程池ThreadPoolExecutor源码剖析
概览
实现runnable接口
实现callable接口,可以接收返回值
自定义线程池或者使用JUC包下已经写好的线程池:线程池方式其实和上面没有区别,只不过上面需要频繁的创建和销毁线程,会造成一些不必要的额外资源消耗,所以在实际开发中肯定会采用线程 池的方式 Java中的Executors自带了一些创建线程池的方式
Executors类下:
java.util.concurrent.ThreadPoolExecutor是自定义线程池的类:
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:
* {@code corePoolSize < 0}
* {@code keepAliveTime < 0}
* {@code maximumPoolSize <= 0}
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数
long keepAliveTime, //空闲线程存活时间
TimeUnit unit, //时间单位
BlockingQueue<Runnable> workQueue, //阻塞队列
ThreadFactory threadFactory, //线程工厂
RejectedExecutionHandler handler) // 拒绝策略
corePoolSize 线程池核心线程大小 线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数 量即是corePoolSize。
maximumPoolSize 线程池最大线程数量 一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后 面会介绍)中,如果工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队 列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。
keepAliveTime 空闲线程存活时间。 一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime 来设定。
unit 空闲线程存活时间单位 keepAliveTime的计量单位
workQueue 工作队列 新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:
1,ArrayBlockingQueue 基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到 corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已 经达到maxPoolSize,则会执行拒绝策略。
2,LinkedBlockingQueue 基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize 后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用 的。
3,SynchronousQueue 一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该 任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。 4,PriorityBlockingQueue 具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
threadFactory 线程工厂 创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。
handler 拒绝策略 当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略, 就是解决这个问题的,jdk中提供了4中拒绝策略:
PS:以下方法都是在java.util.concurrent.ThreadPoolExecutor类下
1,属性
//原子类,用于保存线程池的状态和工作线程的数量。总共32位,前3位表示线程池的状态,后29位表示工作线程的数量
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;
// 线程池的状态,保存在前三位
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;
// ctl的构造和 拆分其得到线程池的状态和工作线程的数量
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; }
2,execute方法
public void execute(Runnable command) {
//健壮性判断
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//如果工作线程的数量小于核心线程数,则创建线程处理任务,addWorker之前都要检查线程池状态
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
//防止多线程情况下c变成其他值
c = ctl.get();
}
//核心线程已满,如果线程池处于运行状态,将任务放进阻塞队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//重复检查,如果不是运行状态,移除队列中的任务,拒绝任务。addWorker之前都要检查线程池状态
if (! isRunning(recheck) && remove(command))
reject(command);
//如果工作线程为0,就添加一个线程,避免出现队列任务没有线程执行的情况。
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//添加线程处理这个任务,如果失败则拒绝。
else if (!addWorker(command, false))
reject(command);
}
3,addWorker方法
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 仅在必要时检查队列是否为空。
if (rs >= SHUTDOWN && // 线程池属于关闭状态,无法接受新任务,也无法处理池中任务,
! (rs == SHUTDOWN && // !SHUTDOWN,即是STOP,TIDYING,TERMINATED
firstTask == null && // firstTask不为空 -> 这里对应上述的addWorker(null,false)
! workQueue.isEmpty()))// 工作队列为空
return false;
for (;;) {
int wc = workerCountOf(c);
//工作线程数再判断
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//通过CAS操作增加工作线程数
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// CAS 由于workerCount 变化而失败;重试内循环
}
}
//开始添加工作线程
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
//判断t!=null的目的,防止t创建失败
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();//加锁的原因就是largestPoolSize,
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||//线程池运行中
(rs == SHUTDOWN && firstTask == null)) {//处于shutdown状态,firstTask == null这里对应上述的addWorker(null,false)
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);//workers:线程池中所有工作线程的集合,
int s = workers.size();
// 如果任务线程大于记录的当前出现过的最大线程数,替换一下。
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//工作线程添加成功
if (workerAdded) {
t.start();//开始执行
workerStarted = true;
}
}
} finally {
if (! workerStarted)
//添加失败,处理方式
addWorkerFailed(w);
}
return workerStarted;
}
4,addWorkerFailed方法,添加线程失败方法
//通过上面的代码,总结哪些情况会出现添加失败的情况:
1,线程池不在运行状态
2,线程已经启动
这2种情况都没有添加成功 workers.remove(w);不会有问题。
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();//因为decrementWorkerCount而加锁
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
//重新检查线程池终止情况,以防此线程存在影响线程池终止。
tryTerminate();
} finally {
mainLock.unlock();
}
}
private final class Worker extends AbstractQueuedSynchronizer implements Runnable
{
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
Worker(Runnable firstTask) {
setState(-1); // 添加标识,worker运行前,禁止中断(AQS)
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** 具体运行调用外部(threadPoolExecutor)的方法 */
public void run() {
runWorker(this);
}
// Lock methods AQS的状态
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
java.util.concurrent.ThreadPoolExecutor#runWorker方法
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//任务不为null,就一直循环,否则调用getTask尝试从阻塞队列获取任务
while (task != null || (task = getTask()) != null) {
w.lock();// 加锁的目的是表示当前任务正在执行,你shutdown任务也不会中断
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);//前置操作
Throwable thrown = null;
try {
task.run();//任务执行
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
// 任务执行的后置处理
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
getTask方法:
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//
if ((wc > maximumPoolSize || (timed && timedOut))//当前工作线程数大于最大线程数 ,后面判断表示是否是允许核心线程超时并且真的超时
&& (wc > 1 || workQueue.isEmpty())) {//工作线程 > 1或者 阻塞队列为空
if (compareAndDecrementWorkerCount(c))// 干掉当前工作线程并返回null,CAS的方式,如果失败,重新走一遍
return null;
continue;
}
try {
Runnable r = timed ?
// 这里是可能出现超时情况并且允许回收线程,那就阻塞这么久拿阻塞队列的任务
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
//这里是线程阻塞在这,等待任务,不参与回收的情况,直到触发signal方法被唤醒,走catch继续下次循环
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
processWorkerExit方法:
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // 如果停止,减一个工作线程数
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();//加锁为了移除工作线程workers.remove(w);
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
tryTerminate();// 尝试干掉线程池
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
// 如果不是认为停止,需要判断线程是否需要追加一个线程处理任务
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;// 查看核心线程是否允许超时
if (min == 0 && ! workQueue.isEmpty())
min = 1;// 如果允许超时,并且工作队列不是空,就将min设置为1
if (workerCountOf(c) >= min)// 如果工作线程数量大于核心线程数,就直接结束
return; // replacement not needed
}
addWorker(null, false);
}
}
大致流程描述:
首先getTask是在一个无限的for循环里面的, 判断 当前运行的线程数<=核心线程数 ,直接调用阻塞队列的take方法,无限阻塞下去,直到队列中有数据放入,唤醒退出for循环。
如果 当前运行的线程数> 核心线程数 ,如果队列没数据,会阻塞 keepAliveTime 时间才返回,继续for循环时,如果队列为空就退出for,销毁该阻塞在队列上的线程。
当任务提交时,如果当前正在运行的线程数 小于 核心线程数 时, 构造一个worker,并且调用worker的start开启一个线程。 线程的run方法为一个while循环。循环里面调用getTask从全局阻塞队列中取出数据,取出就立即执行,然后阻塞到getTask方法的队列上。
当任务提交时,如果 当前正在运行的线程数 大于等于 核心线程数 且 队列没有满,放入到全局阻塞队列里面,此时上面getTask 阻塞的线程将唤醒执行业务任务。
当任务提交时,如果 当前正在运行的线程数 大于等于 核心线程数 且 队列满了,会继续判断当前正在运行的线程数 的是否超过最大线程数,如果超过了,走拒绝任务策略,否则构造一个worker ,调用worker的start开启一个线程,执行第一步的while循环getTask的操作。
getTask时,如果当前运行线程数在核心线程数内,线程不紧张时,就会无限阻塞到队列上,直到队列有数据放入就返回执行,如果当前运行线程数大于了核心线程数,线程开的较多了。就会阻塞keepAliveTime时间,期间如果队列还没数据来,就会销毁这个worker的线程,让其回收。
keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。该参数只在线程数大于 corePoolSize时才有用, 超过这个时间的空闲线程将被终止;但是如果设置 allowCoreThreadTimeOut 为true,则核心线程数到了keepAliveTime时间没任务执行的话也会被销毁掉。 如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。