ThreadPoolExecutor源码分析二
接上文,这里继续分析源码
private static final int COUNT_BITS = Integer.SIZE - 3; private static final int CAPACITY = (1 << COUNT_BITS) - 1; // runState is stored in the high-order bits 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;
线程池有5种状态,RUNNING运行中状态;SHUTDOWN关闭中状态;STOP停止状态;TIDYING收拾整理状态;TERMINATED结束状态。
RUNNING状态是线程池初始化时的状态,可接收任务,可执行任务;调用方法shutdown()变成SHUTDOWN状态,调用方法shutdownNow()变成STOP状态。
SHUTDOWN状态不会再接收任务,但是会把线程中的任务和队列中的任务执行完成。任务执行完后变成TIDYINGZ状态,同时期间调用方法shutdownNow()变成STOP状态。
STOP状态不会接收任务,终止线程中的任务执行,不在执行任务队列里的任务。任务执行完后变成TIDYING状态
TIDYING状态已经不接收任务,不执行线程了。调用钩子方法terminated()后变成TERMINATED状态
TERMINATED状态是一个结束状态。
COUNT_BITS是一个移位标记数,值为29;
CAPACITY = (1 << COUNT_BITS) - 1 // 32位bit,000(前三位) | 后面29为用来记录当前任务数
表示线程池当前接收的任务数最大值为(1 << COUNT_BITS) - 1
状态标记值分别为:
RUNNING = -1 << COUNT_BITS; // 32位bit,111(前三位)| 后面29位全部为0
SHUTDOWN = 0 << COUNT_BITS; // 32位bit,000(前三位)| 后面29位全部为0
STOP = 1 << COUNT_BITS; // 32位bit,001(前三位)| 后面29位全部为0
TIDYING = 2 << COUNT_BITS;// 32位bit,010(前三位)| 后面29位全部为0
TERMINATED = 3 << COUNT_BITS; // 32位bit,011(前三位)| 后面29位全部为0
这里线程池状态标记和当前任务数,使用了一个技巧,后面讲到。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); // Packing and unpacking 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; }
ctl是线程池的一个标记值,同时记录线程池的状态值和当前任务数,32位中,前三位用来记录线程池的状态值,后29位用来记录当前任务数。
runStateOf(int c) { return c & ~CAPACITY; } // 获得当前线程池的状态
workerCountOf(int c) { return c & CAPACITY; } // 获得当前任务数
ctlOf(int rs, int wc) { return rs | wc; } // 构造线程池的标记值
这里很技巧的使用了位运算和存储,大部分java为第一语言的人都没有这种意识。
// 判断ctl值c是否最少是s状态, private static boolean runStateLessThan(int c, int s) { return c < s; } // 判断ctl值c至少是s状态 private static boolean runStateAtLeast(int c, int s) { return c >= s; } // 判断ctl值是否是RUNNING状态 private static boolean isRunning(int c) { return c < SHUTDOWN; }
从状态值能看到 RUNNING < 0 = SHUTDOWN < STOP < TIDYING < TERMINATED,任务数是存储在后29位,他的数量不会影响前面状态对应ctl值的排序。
/** * 使用CAS方式,使ctl中的任务数加一 */ private boolean compareAndIncrementWorkerCount(int expect) { return ctl.compareAndSet(expect, expect + 1); } /** * 使用CAS方式,使ctl中的任务数减一 */ private boolean compareAndDecrementWorkerCount(int expect) { return ctl.compareAndSet(expect, expect - 1); } /** * 使用CAS方式,使ctl中的任务数减一,直到成功为止 */ private void decrementWorkerCount() { do {} while (! compareAndDecrementWorkerCount(ctl.get())); }
下面看看添加任务的方法:
public void execute(Runnable command) { // 校验入参的合法性 if (command == null) throw new NullPointerException(); // 获得ctl的值 int c = ctl.get(); // workerCountOf(c) 获得ctl中标记的任务数 // 当前任务数小于corePoolSize数 if (workerCountOf(c) < corePoolSize) { // 添加任务到执行队列,添加成功即方法结束 if (addWorker(command, true)) return; // 如果多线程竞争失败后,重新获得ctl的值,往下执行 c = ctl.get(); } // 此时执行线程数已经达到corePoolSize数 // 判断线程池是否在运行,任务是否能成功加入到等待队列 if (isRunning(c) && workQueue.offer(command)) { // 成功加入,再次获取ctl值 int recheck = ctl.get(); // 再次判断线程池是否在运行,如果不在运行,则移除任务,并且拒绝任务 if (! isRunning(recheck) && remove(command)) reject(command); // 拒绝任务 // 如果任务数为0,表示执行线程也没有,需要添加一个null的执行任务,实际是添加一个执行线程 else if (workerCountOf(recheck) == 0) addWorker(null, false); // 添加执行线程 } // 任务添加队列失败,看是否可以再添加一个执行线程,执行线程数在(corePoolSize, maximumPoolSize)之间 else if (!addWorker(command, false)) reject(command); // 如果添加失败,也拒绝任务 }
上面有个方法没涉及到,就是 addWorker(Runnable firstTask, boolean core), 下面分析下:
core是标记添加的执行线程是否是核心线程。前面介绍过两个参数corePoolSize, maximumPoolSize,corePoolSize <= maximumPoolSize ;当执行线程数小于corePoolSize时,添加任务时,core=true;当执行线程数在corePoolSize和maximumPoolSize之间时,core=false;当执行线程数大于maximumPoolSize时,core=false,其实是添加不进去的
private boolean addWorker(Runnable firstTask, boolean core) { // retry循环 retry: for (;;) { // 获得线程池ctl的值 int c = ctl.get(); // 获得线程池的状态 int rs = runStateOf(c); // 当线程池的状态至少是停止中,且当不是(rs == SHUTDOWN &&firstTask == null && ! workQueue.isEmpty())时,不让添加执行任务了 // rs == SHUTDOWN &&firstTask == null && ! workQueue.isEmpty() 表示线程池状态为SHUTDOWN,且等待任务队列不为空和添加的任务为null时,还是需要执行线程来执行下面的任务,允许添加执行线程 if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; // 无线循环 for (;;) { // 当前任务数 int wc = workerCountOf(c); // 当当前任务数大于最大任务阀值,或者 wc大于调用方预期的最大值时,添加失败 if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; // 任务添加成功,增加任务数 if (compareAndIncrementWorkerCount(c)) break retry;// 任务数添加成功,中断循环 // 任务数添加有竞争,添加失败,重新获取ctl的值 c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) continue retry; } } // 标记执行线程是否启动 boolean workerStarted = false; // 标记任务是否添加成功 boolean workerAdded = false; Worker w = null; try { // 创建一个执行任务对象 w = new 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()); // 当线程池是RUNNING运行状态 或者 线程池是SHUTDOWN状态,任务是null,往下执行 if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { // 线程必须保证是没有start的,这里做参数校验 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(); workerStarted = true; } } } finally { if (! workerStarted) addWorkerFailed(w); } return workerStarted; }
这里在说明下任务对象,线程池把任务封装成一个对象 java.util.concurrent.ThreadPoolExecutor.Worker,Worker继承接口Runnable,run方法重写如下:
public void run() { // 任务执行线程,其实是在执行线程池的 runWorker方法 runWorker(this); }
后面分析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(); // 如果线程池的状态是STOP,且线程还没有 interrupted, 线程需要再调用 interrupted()方法 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 (;;) { // 获得线程池ctl的值 int c = ctl.get(); // 获得线程池的状态值 int rs = runStateOf(c); // 如果线程池的状态是STOP停止状态,或者线程池的状态是停止中且任务队列为空 if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { // 线程任务数将减一 decrementWorkerCount(); return null; } // 再次获得当前任务数 int wc = workerCountOf(c); // timed表示是否允许获取任务超时就停止线程的运行 // 一种情况是用户设定允许,调用方法:allowsCoreThreadTimeOut() // 另一种情况是用户当前线程数大于corePoolSize 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; } } }
松下问童子,言师采药去。
只言此山中,云深不知处。