ThreadPoolExecutor详解
ThreadPoolExecutor是JDK并发包提供的一个线程池服务,基于ThreadPoolExecutor可以很容易将一个Runnable接口的任务放入线程池中。
ThreadPoolExecutor的构建参数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
参数解释
corePoolSize:核心线程数,会一直存活,即使没有任务,线程池也会维护线程的最少数量;
maximumPoolSize: 线程池维护线程的最大数量 ;
keepAliveTime:线程池维护线程所允许的空闲时间,当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0;
unit:线程池维护线程所允许的空闲时间的单位、可选参数值为:TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS;
workQueue:线程池所使用的缓冲队列,常用的是:java.util.concurrent.ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue,性能方面:线程少 (<20) ,Queue长度短 (<30) , 使用SynchronousQueue ;线程多(>20),Queue长度长(>30),使用LinkedBlockingQueue;如果将来需要进行扩展还是选择LinkedBlockingQueue好,尽量把SynchronousQueue限制在特殊场景中使用;
handler:线程池中的数量大于maximumPoolSize,对拒绝任务的处理策略,默认值ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor任务拒绝策略:
ThreadPoolExecutor内部有实现4个拒绝策略RejectedExecutionHandler:
(1)CallerRunsPolicy,由调用execute方法提交任务的线程来执行这个任务,如果线程池关闭了,这个任务将会被丢弃;
(2)AbortPolicy,抛出异常RejectedExecutionException拒绝提交任务;
(3)DiscardPolicy,直接抛弃任务,不做任何处理,源码中是空的,无任何处理代码;
(4)DiscardOldestPolicy,去除任务队列中的第一个任务,重新提交到队列尾部;
execute方法源码
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
ThreadPoolExecutor中的ctl变量:
ctl是一个AtomicInteger类型的变量,它包含两个概念:
1.workerCount:表明当前有效的线程数,wc
2.runState:表明当前线程池的状态,rs,是否处于Running,Shutdown,Stop,Tidying,Terminate五种状态。
为了把这两种状态放到一个int值中保存,代码中限定了workerCount的值是2^29-1,因为还有五种状态需要表示,需要3位才能表示五种状态,所以会有29位来表示workerCount,而剩下的3位来表示当前线程池的状态。所以说COUNT_BITS为Integer.SIZE(32位)- 3,即29位。CAPACITY = (1 << COUNT_BITS) - 1,为28个1;
1.如果当前线程数小于corePoolSize,调用addWorker方法增加核心线程数(core为true是增加核心线程数,为false是增加非核心线程数),成功则返回,若失败则说明不符合创建核心线程数条件(如线程状态不对等);
2.如果当先线程数大于等于corePoolSize,将command插入缓冲队列,成功则执行完毕;
3.如果2中if判断失败,则说明缓存队列已满,进入if else中,增加非核心线程数,如果目前线程数大于等于maximumPoolSize,则抛出异常;
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
ThreadPoolExecutor.Worker w = null;
try {
w = new ThreadPoolExecutor.Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
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)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();//在被构造为Worker工作线程,且被加入到工作线程集合中后,执行线程任务,注意这里的start实际上执行Worker中run方法,其中实际调用了runWorker方法
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
1.获取当前线程池的状态,如果是STOP,TIDYING,TERMINATED状态的话,则会返回false,如果现在状态是SHUTDOWN,但是firstTask不为空或者workQueue为空的话,那么直接返回false。
2.通过自旋的方式,判断要添加的Worker是否是corePool,如果是的话,那么则判断当前的workerCount是否大于corePoolsize,否则则判断是否大于maximumPoolSize,如果满足的话,说明workerCount超出了线程池大小,直接返回false。如果小于的话,那么判断是否成功将WorkerCount通过CAS操作增加1,如果增加成功的话。则进行到第3步,否则则判断当前线程池的状态,如果现在获取到的状态与进入自旋的状态不一致的话,那么则通过
continue retry
重新进行状态的判断。3.如果满足了的话,那么则创建一个新的Worker对象,然后获取线程池的重入锁后,判断当前线程池的状态,如果当前线程池状态为STOP,TIDYING,TERMINATED的话,那么调用decrementWorkerCount将workerCount减一,然后调用tryTerminate停止线程池,并且返回false。
4.如果状态满足的话,那么则在workers中将新创建的worker添加,并且重新计算largestPoolSize,然后启动Worker中的线程开始执行任务。
5.重新Check一次当前线程池的状态,如果处于STOP状态的话,那么就调用interrupt方法中断线程执行。
final void runWorker(ThreadPoolExecutor.Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// 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);
}
}
这里会先执行beforeExecute,最后执行afterExecute;在runWorker中不断循环getTask去workQueue中获取线程对象,直到都循环完毕;
线程是如何重复利用的
runWoker取任务有两个方式:
- firstTask:这是指定的第一个runnable可执行任务,它会在Woker这个工作线程中运行执行任务run。并且置空表示这个任务已经被执行。 这也是为什么task的run方法只能执行一次的原因
- getTask():这首先是一个死循环过程,工作线程循环直到能够取出Runnable对象或超时返回,这里的取的目标就是任务队列workQueue,对应刚才入队的操作,有入有出。
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())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 最关键的一句
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
线程池的线程复用:就是任务在并不只执行创建时指定的firstTask第一任务,还会从任务队列的中自己主动取任务执行,而且是有/无时间限定的阻塞等待,保证线程的存活。
getTask
的两种取任务的方式:
- 无限定时间:默认不允许core核心线程超时allowCoreThreadTimeOut=false(允许的话就是所有的线程都有超时机制),以及判定当前是core核心线程,没有限定时间的方式,就会一直在
take()
中阻塞直到取出任务。这就是核心线程默认的情况,一直阻塞一直存活一直工作。 - 有限定时间:那就是开启allowCoreThreadTimeOut的核心线程,以及work工作线程,这是keepAliveTime有效作用的唯一地方,它决定了任务线程从任务队列取任务(出队)时
poll()
,最大的等待时间,在这段时间keepAliveTime就是线程的有限的存活时间。如果在限定时间之后取不出任务,就出现超时,对外返回null,最终让当前线程结束。