背景
在进行安卓开发的时候,避免不了要用线程,而谷歌推荐我们使用线程池来进行线程的管理,所以有必要对线程池进行源码的阅读
使用
线程池的一般使用如下
private static final int core_number = Runtime.getRuntime().availableProcessors(); // 线程池里最小保活worker线程数,采用cpu数量
private static final int keep_alive_time = 3; // 每个worker线程最大运行时间
private static final TimeUnit timeUnit = TimeUnit.SECONDS; // 时间单位
private static final BlockingQueue<Runnable> taskQueue = new LinkedBlockingDeque<>(); // 线程池内部的任务线程队列
private static final ExecutorService executor = new ThreadPoolExecutor(core_number, core_number * 2,
keep_alive_time, timeUnit, taskQueue, Executors.defaultThreadFactory()); // 实例化线程池
public static void executeRunnable(Runnable runnable) {
executor.execute(runnable); // 执行自定义线程
}
一切都是从executor.execute(runnable)开始的,对源码的阅读也就从这个方法开始
源码阅读
ThreadPoolExecutor.execute(runnable)源码如下
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException(); // 如果传入的线程是null,直接抛异常
int c = ctl.get(); // 当前worker数量,worker是线程池内部的一个类,用来执行线程队列里的线程
if (workerCountOf(c) < corePoolSize) {
// worker数小于线程池中要保活的最小worker线程数,先尝试直接启动线程
if (addWorker(command, true)) // addWorker()返回true,表示已经执行了线程
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
// offer()方法用来在队尾添加元素,如若队满,则返回false
int recheck = ctl.get(); // 再次检查计数器
if (! isRunning(recheck) && remove(command))
reject(command); // 如果线程池已满并且可以从队列中移除runnable,就抛出异常
else if (workerCountOf(recheck) == 0) // 如果线程池空了,就添加一个默认的worker,但这个worker的初始线程是null
addWorker(null, false);
}
// 在计数器的值已经大于保活数,并且任务队列已满的情况下,再加不进去新线程command,就直接拒绝command
else if (!addWorker(command, false))
reject(command);
}
ctl是原子整数(AtomicInteger),默认值是0,以提供线程安全的计数功能。
worker是线程池内部封装的一个类,也是一个线程,用来进行任务线程的调度执行,当执行线程的run()方法时,会先执行worker的run()方法,线程的run()会在worker的run()里执行。线程池里,多个worker共享一个线程队列。每新传入一个线程,worker集合会add一个新建出来的worker,而当一个worker从任务线程队列中获取到一个任务线程(获取不到就不执行,直接移除)并执行完后,worker集合会将此worker移除出去,并以一个空的worker替换之。
corePoolSize就是我们在构造方法里传入的最小保活worker线程数,当worker数量小于这个数目时,worker从线程队列取线程时,是要限时的,限时时长就是我们在构造方法中传入的keep_alive_time
可见,核心方法有三个:addWorker()、worker.run()、reject()
线程池里直接操作的线程都是worker线程,worker则直接调度任务线程
addWorker()
代码如下
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
// 状态检测和判空,状态不对直接返回false
return false;
// 这个无限循环用来尝试让worker计数器ctl+1,成功就退出retry循环
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false; // worker数量已经满了,直接返回false
if (compareAndIncrementWorkerCount(c))
// 尝试让当前计数器ctl+1,成功的话,退出retry循环
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
// 状态变化,进行下一次retry迭代
continue retry;
}
}
// 到这一步,已经把worker计数器ctl++
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 添加worker,构造方法里会new一个线程,做为任务线程
w = new Worker(firstTask);
final Thread t = w.thread; // new出来的任务线程
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock(); // 获取锁
try {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException(); // 线程池关闭并且传入runnnable是空,但worker中的线程却可运行,状态就不对
workers.add(w); // 添加worker
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s; // 更新已分配的最大worker数
workerAdded = true; // 设置标志位
}
} finally {
mainLock.unlock(); // 释放锁
}
if (workerAdded) {
// 启动任务线程,更新标志位
t.start(); // 这里会先执行worker的run()方法
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w); // 没有成功启动任务线程时的善后处理
}
return workerStarted;
}
执行完retry循环后worker的计数器会++,然后会执行Worker的构造方法来新建一个worker,构造方法代码如下
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask; // 保存传进来的线程任务
this.thread = getThreadFactory().newThread(this); // worker自己也是一个runnable
}
调用了ThreadFactory来构造线程,我们用的是DefaultThreadFactory,它的newThread()代码如下
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false); // 非守护线程
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY); // 普通优先级
return t;
}
worker#run()
回到ThreadPoolExecutor.addWorker()方法,新建完worker后,我们的runnable就会保存为worker的firstTask,在成功把新worker添加到worker集合中后,就会执行worker.thread.start()方法,这个方法其实就是执行worker本身的run()方法,代码如下
public void run() {
runWorker(this);
}
调用的是线程池的runWorker(),传入参数是worker自己
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask; // 获取firstTask,也就是我们的runnable
w.firstTask = null;
w.unlock(); // worker先解锁
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) { // firstTask可能是空,这个时候我们的runnable存储在任务队列中,getTask()方法会尝试从队列中获取线程
w.lock(); // 锁住worker
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 (Exception x) {
.. 异常
} finally {
afterExecute(task, thrown); // 空方法
}
} finally {
task = null;
w.completedTasks++; // worker完成的任务数量+1
w.unlock(); // worker释放锁
}
}
completedAbruptly = false; // 只有发生异常了,completedAbruptly才会是true
} finally {
processWorkerExit(w, completedAbruptly); // 无论执没执行线程任务,都会删除worker
}
}
看一下getTask()是怎么从线程队首获取任务的
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount(); // 如果线程池关闭停止或任务队列是空,worker计数器-1,返回null
return null;
}
int wc = workerCountOf(c);
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; // worker数量大于最大保活worker线程数,就要进行对任务队列的限时出队
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) { // worker数大于最大worker线程数,抑或是超时,结束当前迭代或直接返回空
if (compareAndDecrementWorkerCount(c)) // 能够将worker计数器-1,返回null
return null;
continue;
}
try {
//如果corePool溢出或超时,就限时获取任务队列队首,否则不限时(如果任务队列是空,就阻塞在那儿了)
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r; // 如果能成功获取,就返回
timedOut = true; // 否则,就是超时
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
可以看到,当线程队列是空时,worker线程是可以被阻塞的,它会一直阻塞到线程队列不是空或worker集合满
当获取到线程时,worker从getTask()返回,就会执行线程的run()方法,然后循环读取线程队列,不断进行处理或阻塞
当因线程池关掉、worker队列满、超时等原因而获取到的线程是null,就会退出处理循环,进入ThreadPoolExecutor.processWorkerExit()来清理这个worker
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // 如果是因为抛出异常进来的这个方法,直接把worker数量-1
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks; // 记录所有worker完成任务的数量
workers.remove(w); // 从worker集合里去掉worker
} finally {
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) { // 状态小于stop(shutdown或running)
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize; // 如果设置了allowCoreThreadTimeout,就把min设置成0,否则,就设成最小保活worker数
if (min == 0 && ! workQueue.isEmpty()) // 如果allowCorewThreadTimeout,并且任务队列不是空,就把min改成1
min = 1;
if (workerCountOf(c) >= min) // 如果当前worker数大于min,直接返回,不增加一个空的worker
return; // replacement not needed
}
addWorker(null, false); // 否则就要以一个空的worker代替老的worker
}
}
处理中间,调用了tryTerminate()方法,代码如下
final void tryTerminate() {
for (;;) {
int c = ctl.get(); // worker数目
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return; // 状态检查
if (workerCountOf(c) != 0) {
interruptIdleWorkers(ONLY_ONE); // 中断一个空闲worker线程
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { // 设置状态为tidying
try {
terminated(); // 空方法
} finally {
ctl.set(ctlOf(TERMINATED, 0)); // 更新状态为terminated
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
如果当前线程池的状态>=tidying(tidying或terminated)或者正在运行或者已关闭但任务队列不是空,说明状态不对,直接返回。否则,如果worker队列不是空,就中断一个空闲的worker线程,然后返回。如果worker队列是空,就改变线程池状态为tidying
其中,中断一个空闲的worker线程调用了interruptIdleWorkers()方法,传入参数为true
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();
}
}
就是遍历worker队列,找到一个没有被中断也没有运行的worker线程,由于传入参数onlyOne是true,所以找到一个符合条件的,中断完就返回了。
回到最初的execute(),如果线程池里worker数目已经超过了最小保活数,而且任务队列满,这时再次调用addWorker(),还是加不进去新线程的话,就调用reject()方法,表示添加线程失败
reject()
代码如下
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
调用的是ThreadPoolExecutor内部类AbortPolicy的rejectedExecution()方法,代码如下
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
原来是直接抛出异常。
关于线程池的线程调度机制大致如此。接下来,我再看一看线程池里关于关闭的两个方法shutdown()和shutdownNow()的源代码,了解一下它的关闭机制
shutdown()
代码如下
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess(); // 检查是否允许关闭,但里面调用的securityManager.checkPermission()和checkAccess()都是空方法
advanceRunState(SHUTDOWN); // 设置状态为shutdown
interruptIdleWorkers(); // 中断所有空闲worker线程
onShutdown(); // 空方法
} finally {
mainLock.unlock();
}
tryTerminate();
}
tryTerminate()上文已经说过,这里调用的作用应该就是设置一下线程池的状态是tidying。所以这里主要的方法就是interruptIdleWorkers(),此方法代码如下
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
调用了interruptIdleWorkers()方法,此方法上文已经说过,只不过这里传入的参数是false,也就是要中断所有的空闲worker线程
shutdownNow()
代码如下
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess(); // 空方法
advanceRunState(STOP); // 设置状态是stop
interruptWorkers(); // 中断所有worker线程
tasks = drainQueue(); // 把任务队列清空并返回给外界
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
和shutdown()不同的地方一个是shutdownNow()要把任务队列清空并返回给外界,导致线程池内直接没有了任务,再一个调用了interruptWorkers()方法,这个方法用来中断所有worker线程,不管空不空闲,此方法代码如下
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
遍历调用了worker.interruptIfStarted(),只要线程start了,立马中断,代码如下
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
只要线程启动(调用了runWorker()),getState()就>=0,其余不用多解释了,就是中断线程。
所以shutdown()只是中断空闲线程,并且shutdown状态会导致addWorker()直接返回false,也就是不会添加新的任务,但还是会执行完线程池内现有的任务。但shutdownNow()则是中断了所有worker线程,并且设置状态是stop,这种状态也会导致addWorker()不添加新任务,同时由于中断了所有worker线程,正在执行的任务也会被取消。
结语
线程池内部主要是用了worker集合和任务队列两个列表来管理线程,而worker本身就是一个线程,同时还有一个firstTask属性,也是一个线程,做为这个worker的默认任务,只有默认任务完了,worker才会在任务队列里获取任务来执行。
线程池里另有两个容量:最小保活worker数和最大worker数,在添加任务时,前者是worker集合的最大容量,后者是任务列表的最大容量。如果当前worker线程数小于最小保活worker数,那么直接把新任务做为新worker的firstTask,也就是新worker的默认任务,如果当前worker线程数大于等于最小保活worker数,并且小于最大worker数,说明此时worker集合已满但任务队列未满,所以就把新任务加到任务队列中,等待worker们调用执行。
worker的run()方法会一直尝试执行自身的默认任务或从任务队列中获取任务执行,如果自身没有任务,任务队列也是空,这个worker的run()方法就执行完了。
至于线程池的关闭,结论很简单,shutdown()不再接收新任务,但已有任务会被执行完,shutdownNow()则是不但不接收新任务,现有的任务也会全部被中断。