//存放状态值,shutdown等均可更改
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//29
private static final int COUNT_BITS = Integer.SIZE - 3;
//一共29个1
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;
// Packing and unpacking ctl
//获取线程池的状态rs
private static int runStateOf(int c) { return c & ~CAPACITY; }
//获取线程池中的线程数量
private static int workerCountOf(int c) { return c & CAPACITY; }
//初始化变量ctl,使用两个数或操作初始化,高3位表示状态,低29位表示线程池中线程数量
private static int ctlOf(int rs, int wc) { return rs | wc; }
RUNNING 表示当前线程处于运行状态,可以接受新提交任务
SHUTDOWN 不接受新任务,但是会执行完阻塞队列中待执行的任务,一般是通过shutdown()产生
STOP 不接受新任务,阻塞队列中待执行任务也不执行,一般是通过shutdownNow()产生
TIDYING 线程池中所有的任务已经结束,线程数量为0,开始调用terminated() 临时状态
TERMINATED terminated()方法调用完成
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//判断线程池中线程数是否小于核心线程数,需要创建核心线程来执行任务
if (workerCountOf(c) < corePoolSize) {
//这个true是用来判断是否为核心线程数的
if (addWorker(command, true))
//如果小于核心线程数就返回了
return;
c = ctl.get();
}
//执行这里,说明前面核心线程数已经满了,如果线程池还处于running状态,则尝试将任务放入阻塞队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//可能出现刚刚把任务放入线程池中的任务队列,然后此时线程池被关闭了,那么此时需要把任务从队列中移除(并发情况)
if (! isRunning(recheck) && remove(command))
reject(command);
//这里是什么场景?核心线程数为0了?这就是前面说的我们可以设置核心线程数完成任务后就被销毁,那么核心线程数就为0了,那么刚刚队列中的任务怎么执行呢,就需要使用使用创建非核心线程数来执行任务了(可以忽略,因为不会这么设置)
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//执行到这里,说明核心线程数满了,队列也满了,创建非核心线程来执行任务
else if (!addWorker(command, false))
reject(command);
}
ADDWork
private boolean addWorker(Runnable firstTask, boolean core) {
//java语法,本代码中标记的break直接破开多重循环,标记的continue跟普通break作用一致
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//表示当前线程池处于SHUTDOWN 状态后,新增的任务不为空的,直接返回,代表添加任务失败
//表示当前线程池处于SHUTDOWN 状态后,核心线程数为0,新增非核心线程数来处理任务,但是队列为空,直接返回,代表添加任务失败.
//返回后会继续走非核心线程数
//core是用来判断是否是核心线程
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
//如果当前线程数已经达到最大了CAPACITY了,直接返回
//如果添加的是核心线程,但当前线程数据已经达到core了直接返回
//如果添加的的非核心线程数,但当前线程数已经达到maximumPoolSize直接返回
//前面校验都通过了,开始新增线程池中的线程数量compareAndIncrementWorkerCount(c)通过CAS添加如果失败,进行重试,否则直接跳出外面最大的循环。
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//最开始看这里没看懂,这是CAS,如果当前线程没有成功,就继续等待,整个这块都是在并发条件下阻塞当前线程
if (compareAndIncrementWorkerCount(c))
break retry;
//走到这里说明前面cas失败了,那么需要重新读取
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;
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.
//握住锁时重新检查。
//在ThreadFactory失败或
//在获取锁之前关闭。
//获取当前线程状态
int rs = runStateOf(ctl.get());
//线程在运行或者线程在暂停而且任务不为空
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
//检查一下线程是否能开启
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
//将work添加到线程池集合中
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;
}
接下来看下work
实现了runnable的接口
final void runWorker(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 {
//还是调用了传入的任务的run()
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;什么时候不会被执行?因为这里没有catch,所以
task.run();发生异常
beforeExecute(wt, task); 发生异常
afterExecute(task, thrown); 发生异常
为啥getTask()发生异常没有提到,因为这里面代码实现被try...catach捕获了,不会对外抛出异常。(后面后详细讲这里的实现)
所以一言以蔽之,completedAbruptly = false;只有在用户自己的任务出现异常抛出导致的,手动关闭线程池不会受到影响 completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
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 {
-
这里是线程复用的关键,如果设置了允许核心线程超时,workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)那么超时返回。线程不再被复用
-
如果未设置超时,则一直阻塞等待workQueue.take()
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
//
Retrieves and removes the head of this queue, waiting up to the specified wait time if necessary for an element to become available.
Params:
timeout – how long to wait before giving up, in units of unit
unit – a TimeUnit determining how to interpret the timeout parameter
Returns:
the head of this queue, or null if the specified waiting time elapses before an element is available
Throws:
InterruptedException – if interrupted while waiting
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
接下来看processWorkerExit方法
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
// 如果出现异常,则将线程池中线程数量-1
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
//如果当前线程处于运行running 或者shutdown状态
if (runStateLessThan(c, STOP)) {
//非用户任务异常,也就是手动执行的中断操作前面有提到过
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
//如果队列中还有待执行的任务,那么必须要保证线程池中至少有一个线程,否则就创新一个新的非核心线程
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
线程池中的线程是如何复用的? while (task != null || (task = getTask()) != null)
我们传入的任务如果不为空,那么就执行传入的任务,执行完成后,就从队列中拿任务执行(task = getTask())。如队列中为空,那么就会阻塞,线程不会被销毁。
如果我们提交的任务抛异常了,线程池中的线程会被终止销毁吗?
如果出现任务异常,原线程结束,会新启一个新的线程,也就是线程池中的线程数量不会受到任务异常的影响。
接下来分析停止方法
shutdown() 不接受新任务,但是执行队列中的任务
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
//修改线程池状态位SHUTDOWN
advanceRunState(SHUTDOWN);
//中断空闲线程,何为空闲线程?
interruptIdleWorkers();
//钩子方法,等待子类实现,默认为空方法
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
这里是中断空闲线程,那么如何区分出哪些是空闲线程呢。通过w.tryLock(); 能获取锁成功的,表示是空闲线程,因为如果线程在支线任务时,也必须获取到锁,而处于阻塞等待的线程是没有获取到锁的。
那么问题来了,这里只是把空闲线程中断停了,那么那些非空闲线程执行完任务后,是如何停止的呢?
正常线程执行完任务后,就会getTask()阻塞、这里面有段代码。当线程池状态为SHUTDOWN且任务队列为空,则直接返回空,这样线程就能正常退出来了
接下来的学习流程要根据问题来学习
-
什么是线程池,为什么在Android中使用线程池?
-
Android中线程池的主要优势是什么?
-
请解释线程池中的核心线程数、最大线程数、保持活动时间和工作队列的作用。
-
在Android中,如何创建一个线程池?
-
什么是ThreadPoolExecutor,它是如何工作的?
-
线程池中的任务提交方式有哪些?
-
什么是Runnable和Callable接口,它们在线程池中的使用有什么不同?
-
在什么情况下你会选择使用FixedThreadPool,CachedThreadPool或ScheduledThreadPool?
-
如何处理线程池中的异常?
-
什么是线程池的拒绝策略,有哪些常见的拒绝策略?
-
请解释Android中的AsyncTask,它与线程池有什么关系?
-
如何实现一个带有优先级的线程池?
-
如何监视和调整线程池的性能?
-
什么是核心线程数和非核心线程数的区别?
-
你如何在Android中处理长时间运行的任务,以避免ANR(Application Not Responding)?
-
如何确保线程池中的任务按照正确的顺序执行?
-
请解释线程池中的工作队列是如何工作的,以及有哪些不同类型的工作队列?
-
在Android中,如何取消线程池中的任务?
-
什么是线程池的最佳实践,包括线程池的大小和配置建议?
-
请解释线程池的资源管理,包括线程的创建、销毁和重用过程。
我们根据这些问题提问,线程池的线程是怎么设置的?
那么再次复习源码
首先一个问题,线程池的线程 可不可以是一个单独线程?而不是并发开启多个线程?
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
这个 ThreadFactory 是一个接口里面有一个new Thread方法,你可以复写它把它做成单例的,之后就可以所有的任务在一个线程跑了
public interface ThreadFactory {
/**
* Constructs a new {@code Thread}. Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to
* create a thread is rejected
*/
Thread newThread(Runnable r);
}
接下来是execute方法 如果运行的线程数少于核心线程数(corePoolSize),则尝试启动一个新的线程,并将给定的任务作为其第一个任务。如果一个任务可以成功排队,那么我们仍然需要再次检查是否应该添加一个线程(因为现有的线程在上次检查后可能已经死掉),或者线程池是否在进入此方法后关闭了。如果我们无法排队任务,那么我们尝试添加一个新的线程。如果失败,我们知道我们已经关闭或饱和了,因此会拒绝任务。这3个if就是这3件事
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
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);
}
然后是一个核心的方法addWork 这里有个问题,为什么先进行CAS再进行加锁
addWorker
方法先进行CAS(比较并交换)操作,然后在某些情况下再进行加锁,是为了尽量减少对锁的使用,以提高并发性能。这是一种常见的优化策略,通常称为"锁分离"或"锁分段"。
CAS操作是一种非阻塞的原子操作,它不会引入线程的阻塞,而是根据当前值与期望值的比较来尝试修改值。如果CAS成功,那么线程可以继续执行,否则它会重试直到成功为止。由于CAS操作不涉及线程的阻塞,因此它可以在不引入锁的情况下实现一些并发控制逻辑。
在addWorker
方法中,先进行CAS操作是为了尽早地尝试增加工作线程,以提高并发性能。只有在CAS操作失败的情况下,才需要进行加锁,以确保线程池的状态一致性。这种方式可以减少线程竞争锁的概率,减少了锁的争用,提高了线程池的性能。
还有一个问题就是 一个线程可以多次start嘛?
如果尝试多次调用同一个线程对象的start()
方法,将会抛出IllegalThreadStateException
异常。
这是因为一旦线程启动,它就进入了执行状态(RUNNABLE
状态),并开始执行与线程相关联的代码块(通过线程的run()
方法)。在这个执行过程中,如果再次尝试启动同一个线程,会破坏线程的状态和执行,可能导致不可预测的结果。
所以说,addWork就是一个开启新线程并且执行的一个方法
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
首先进行CAS
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;
Worker w = null;
try {
在这里进行new 一个线程,new 线程的策略可以自己制定
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
加锁,那么有个问题,为什么先进行CAS再进行加锁
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();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
那么是如何进行阻塞线程不被销毁的?
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
这个getTask 方法才是关键,getTask之后会通过阻塞对列的await进行阻塞
等到获取的时候再进行释放线程
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);
}
}