问题是最好的老师。
备注:问题统一在最后进行总结,然后在源码分析的过程中会穿插分析。
//通过该变量同时存储线程池状态和工作线程数量,通过runStateOf(ctl)获取状态,workerCountOf(ctl)获取工作线程数量。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//32-3=29,线程池状态用int类型32位二进制的高3位表示,请看下文。
private static final int COUNT_BITS = Integer.SIZE - 3;
//线程池最大容量:0001 1111 1111 1111 1111 1111 1111 1111,即最高三位用来存储线程池状态,剩下29位用来存储线程池数量。
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
//线程池共五种状态,分别用int的高三位表示。具体状态的意义等看完源码之后,在思考的总结中再做解释。
//1110 0000 0000 0000 0000 0000 0000 0000,即111(负数用补码表示)。
private static final int RUNNING = -1 << COUNT_BITS;
//0000 0000 0000 0000 0000 0000 0000 0000,即000。
private static final int SHUTDOWN = 0 << COUNT_BITS;
//0010 0000 0000 0000 0000 0000 0000 0000,即001。
private static final int STOP = 1 << COUNT_BITS;
//0100 0000 0000 0000 0000 0000 0000 0000,即010。
private static final int TIDYING = 2 << COUNT_BITS;
//0110 0000 0000 0000 0000 0000 0000 0000,即011。
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl
//这里主要借助了如下思想:
//1.X&1=X,即1和任何数X逻辑与等于X。
//2.X|0=X,即0和任何数X逻辑异或等于X。
//已知CAPACITY=0001 1111 1111 1111 1111 1111 1111 1111,则~CAPACITY=1110 0000 0000 0000 0000 0000 0000 0000
//通过ctl获取线程池运行状态,高三位。请借助思想1自己分析。
private static int runStateOf(int c) { return c & ~CAPACITY; }
//通过ctl获取工作线程数量,低29位。请借助思想1自己分析。
private static int workerCountOf(int c) { return c & CAPACITY; }
//将运行状态和工作线程数量打包成一个字段。请借助思想2自己分析。
private static int ctlOf(int rs, int wc) { return rs | wc; }
//是否允许核心线程超时,可以理解为线程池进行线程服用就是复用的核心线程,其复用的原理就是如果没有任务需要执行时,则阻塞该线程,这个参数控制是否允许阻塞超时。
private volatile boolean allowCoreThreadTimeOut;
//如果设置了allowCoreThreadTimeOut为true或者当工作线程数大于核心线程数时,线程阻塞等待时间,过了这个时间如果还没有任务执行,则该线程结束。
private volatile long keepAliveTime;
//核心线程数量。
private volatile int corePoolSize;
//最大线程数量。
private volatile int maximumPoolSize;
//当工作线程数到达核心线程数时,将提交的任务添加到阻塞队列。
private final BlockingQueue workQueue;
//线程工厂,可以自定义线程池。
private volatile ThreadFactory threadFactory;
//拒绝策略,当工作线程数大于最大线程数时,线程池对于提交的任务采用的处理方式。
private volatile RejectedExecutionHandler handler;
//执行提交的任务
//command:要执行的任务
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//如果工作线程数量小于核心线程数,则开启新的线程处理该任务。
if (workerCountOf(c) < corePoolSize) {
//开启新的线程,请看addWorker方法。
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);
//当存在以下情况,如果工作线程为0(当线程池设置了允许阻塞超时,且已经超时),添加一个work执行队列中的任务。
//1.线程池仍然处于运行状态。
//2.线程池处于关闭状态。但是将任务移出队列失败,此时队列不为空。
else if (workerCountOf(recheck) == 0)
//addWorker方法中出现的firstTask为null的情况在这里。
addWorker(null, false);
}
//当队列中已满时,采用最大线程数进行判断。如果工作线程数小于最大线程数。则继续开启一个线程处理。否则采用拒绝策略处理。
else if (!addWorker(command, false))
reject(command);
}
其继承体系如下:
private final class Worker extends AbstractQueuedSynchronizer implements Runnable
/** Thread this worker is running in. Null if factory fails. */
//所要开启的线程。该线程会被复用。
final Thread thread;
/** Initial task to run. Possibly null. */
//第一个将要执行的任务。
Runnable firstTask;
Worker(Runnable firstTask) {
//禁止中断直到运行了Worker。具体请看下面interruptIfStarted方法。
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
//开启一个新的线程。
this.thread = getThreadFactory().newThread(this);
}
public void run() {
//调用了外部类的runWorker方法。
runWorker(this);
}
//运行Worker。
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
//将state设置为0(构造函数中设置为-1),允许中断。
w.unlock(); // allow interrupts
//突然完成。
boolean completedAbruptly = true;
try {
//线程复用的原理即在这里,循环判断:
//1.如果firstTask不为空,先执行第一个任务。
//2.firstTask为空,队列中任务不为空,执行队列中的任务。具体请看getTask()方法,该方法实现了线程阻塞。
//3.如果未阻塞或者阻塞时间已到且从队列中未获取到任务任务,则该线程终止。
while (task != null || (task = getTask()) != null) {
//设置state为1,独占锁。
w.lock();
//再次判断线程池状态,如果线程池状态大于等于STOP且该线程未被中断,则中断该线程。
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);
}
}
//线程中断只能在state大于等于0才可以。
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
//从队列中获取任务并根据线程池参数判断是否进行阻塞。
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.
//判断线程池状态,大于等于STOP,即使队列中有任务也不执行。
//如果状态为SHUTDOWN,队列不为空,继续执行,为空不执行。
//当该线程不再执行任何任务时,减少工作线程数。
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
//线程是否会阻塞超时,当存在以下情况则认为会超时。
//1.设置了允许核心线程数超时。
//2.当工作线程数大于核心线程数(这里可以解释问题四中的最大线程数是如何降为核心线程数的)。
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//同时满足以下情况,认为需要将该线程终止:
//1.工作线程数大于最大线程数或者允许线程阻塞超时且已经阻塞超时。
//2.工作线程数大于1且队列为空。
//这里的timedOut第一次进来必然为true,是通过下文的keepAliveTime来综合判断设置的,请看下文。当keepAliceTime时间到,就会变成true。
//这里工作线程数大于最大线程数,注释解释为due to a call to setMaximumPoolSize,即在线程池执行的过程中,可能重新设置了最大线程数。新设置的值比原值小。
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
//当判断可以终止该线程,但是CAS失败的时候,说明已经有其它线程进行了CAS操作,进行重试。因为可能其它线程CAS操作之后线程数已经不大于核心线程数了,那么如果未设置allowCoreThreadTimeOut为true,该线程就没必要终止。
continue;
}
try {
//截止到这里说明线程池状况如下:
//1.队列不为空。
//2.该线程还未阻塞超时,即还未进行阻塞操作。
//3.工作线程数大于最大线程数(为什么出现这种请看上文有解释)。
//这里通过阻塞队列的两个方法分别实现不超时阻塞和超时阻塞。
//poll:等待keepAliveTime时间,如果队列中仍然没有任务,则返回为空。
//take:如果队列中不存在任务,则一直阻塞。
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
//当获取到的任务为空,则说明阻塞时间到,设置timeOut未true,再次循环,就会走到上文的逻辑,成功结束该线程。
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
//关闭线程池,使线程池处于SHUTDOWN状态。
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//权限检查,JDK默认的权限检查为空。
checkShutdownAccess();
//设置线程池状态为SHUTDOWN。
advanceRunState(SHUTDOWN);
//中断空闲的线程,具体请看下文该方法分析。
interruptIdleWorkers();
//钩子函数,重写线程池时可以重写该方法。
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
//尝试终止线程池。请看下文对该方法分析。
tryTerminate();
}
//当worker.tryLock成功时,将该worker视为空闲线程
//因为worker如果为运行状态,该worker必定持有持有锁(即state为1),请看runWorker方法,其调用了lock方法,设置state为1。
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
try {
//中断线程
t.interrupt();//中断线程后会发生什么情况。
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
//尝试将线程池设置为终止TERMINATED状态。
final void tryTerminate() {
for (;;) {
int c = ctl.get();
//当存在以下情况时,将线程池推到终态。
//前提:线程池不为RUNING,线程池并非已经到终止状态。
//1.线程池为STOP且线程池为空(工作线程数为0)。
//2.线程池为SHUTDOWN队列为空且线程池为空。
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
//空方法。
terminated();
} finally {
//设置为终态。
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
//关闭线程池,使其处于STOP状态。
public List shutdownNow() {
List tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
//不同shutdown的是,这里直接中断线程,即使线程是运行的,而shutdown只会中断idle线程。
interruptWorkers();
//移出阻塞队列中的所有任务。
tasks = drainQueue();
} finally {
mainLock.unlock();
}
//见上文,尝试终止线程池。
tryTerminate();
return tasks;
}
线程池共提供了四种拒绝策略,都实现RejectedExecutionHandler接口,重写rejectedExecution方法,默认的拒绝策略为AbortPolicy,接下来我们看一下各种策略的实现方式和不同点:
//直接抛出异常
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
//如果线程池未关闭,则由提交该任务的线程执行该任务。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
//如果线程池未关闭,则将阻塞队列队头任务移出,然后重新提交任务到线程池
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
//直接放弃执行该任务
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
答:主要有:
1.复用线程。避免频繁创建和销毁线程所带来的开销。
2.更好的管理线程,使系统可控。
答:源码中已经分析过。可以自己根据源码分析画个流程图出来。
答:具体请看getTask()方法,首先取决于我们是否设置了allowCoreThreadTimeOut参数,如过设置为true,那么并不能真正做到线程始终复用,因为如果设置该参数,当核心线程在队列中等待keepAliveTime时间之后仍未获取到任务,核心线程变会终止。当未设置该参数时,核心线程会通过BlockQueue.take()方法一直阻塞等待有任务达到,而且当任务达到之后,通过死循环继续获取,从而达到复用的目的。还有一点就是只调用了一次Worker.thread.start(),而每一个任务的运行都是通过调用Runnable的run()方法执行的。
答:当设置了allowCoreThreadTimeOut为true,会变为0,否则会变为核心线程数。最大线程数降为核心线程数在上面源码分析过程中已经讲解,主要在getTask()方法中,判断线程是否允许超时时,有一个条件为wc>corePoolSize,即工作线程数大于核心线程数,即在这种情况下,当前线程变化阻塞超时,最终终止。
通过源码分析,我们看到了线程有两个方法可以终止线程池,分别是shutdown()和shutdownNow()方法。其分别把线程置于SHUTDOWN和STOP状态。而正常的状态为RUNNING状态。所以可以从三个状态分析:
1.RUNNING:正常状态,走正常线程池执行流程。
2.SHUTDOWN:关闭状态,该状态下,新提交的任务将拒绝,但仍然会执行已经存在于阻塞队列中的任务。
3.STOP及以上:终止状态,拒绝新任务,中断所有线程即使正在运行,清空队列。
具体请看博文:【阻塞队列】(https://blog.csdn.net/qq_31331965/article/details/100940842)。
请看上文线程池执行策略。
1.通过ThreadPoolExecutor构造函数手动创建线程池。
2.通过Executors的静态方法构造特定需求的线程池。具体请看博文:Executros类的使用。
具体请看参考博文:http://ifeve.com/how-to-calculate-threadpool-size/。