在我看来 理解线程池是非常有必要的,像一些比较出名的开源 框架都用到了线程池, 比如: Okhttp, EventBus等
对于我个人而言,平时对线程池的使用也只是停留在使用层面, 对其原理了解的少之又少,我自己也很好奇,于是花了点时间研究了一下。
带着如下几个问题对线程池进行了解:
- ThreadPoolExecutor的工作状态
- ThreadPoolExecutor的重要成员变量
- 重要成员变量
- 常见的线程池
里面大量用到了ReentrantLock, 以及CAS的相关知识, 这一块可以自己脑补一下
ThreadPoolExecutor的工作状态
线程池有5种状态
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;
每一种状态的具体含义如下(对于这一块只需要记住即可):
- RUNNING(运行,-1):能够接收新任务,也可以处理阻塞队列中的任务。
- SHUTDOWN(待关闭,0):不可以接受新任务,继续处理阻塞队列中的任务。
- STOP(停止,1):不接收新任务,不处理阻塞队列中的任务,并且会中断正在处理的任务。
- TIDYING(整理,2):所有的任务已终止,ctl记录的“工作线程数”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,执行钩子方法terminated()。对于它的实现,在ThreadPoolExecutor中什么也没做。
- TERMINATED(终止,3):完全终止,且完成了所有资源的释放。
线程池的工作状态是什么时候切换呢
- RUNNING -> SHUTDOWN:在调用ShutDown()时或隐式调用在finalize()中。
- (RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()时。
- SHUTDOWN -> TIDYING:当阻塞和队列和工作线程数都为0时。
- STOP -> TIDYING:当工作线程数为0。
- TIDYING -> TERMINATED:当terminated()钩子方法完成时。
通过源码来验证上面的说法:
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
从上面的源码里面看到了如下的一句代码advanceRunState(SHUTDOWN);
private void advanceRunState(int targetState) {
// assert targetState == SHUTDOWN || targetState == STOP;
for (;;) {
int c = ctl.get();
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
这句代码ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
就是通过 CAS的原理进行值 的修改, 而ctlOf方法会计算出真正的状态值
ThreadPoolExecutor的重要成员变量
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- int corePoolSize--核心线程数
- int maximumPoolSize--最大线程数
- long keepAliveTime--允许线程空闲时间
- TimeUnit unit--时间对象
- BlockingQueue<Runnable> workQueue--阻塞队列
- ThreadFactory threadFactory--线程工厂
- RejectedExecutionHandler handler--任务拒绝策略
ThreadFactory
线程工厂
其作用是创建线程
如果外面不传入线程工厂, 则默认用源码里面的DefaultThreadFactory
RejectedExecutionHandler
拒绝策略
是在线程池执行excute方法的时候, 如果满足一定条件则会执行其方法
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);
}
从上面的代码分析看到了如下的代码reject(command);
而这个方法是真正的执行拒绝策略里面的方法。
从源码分析 拒绝策略有如下几种:
1)直接抛出异常(AbortPolicy)
默认采用,对拒绝任务抛弃处理,并且抛出RejectedExecutionException异常。
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
2)使用调用者的线程来处理(CallerRunsPolicy)
如果当前线程池处于运行状态 ,直接使用当前线程执行任务,如果是终止状态,则直接抛弃。
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
3)直接丢掉这个任务(DiscardPolicy)
对拒绝任务偷偷地抛弃,没有异常信息。查看源码发现他的rejectedExecution ()就是一个空实现。
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
4)丢掉最老的任务(DiscardOldestPolicy)
对拒绝任务不抛弃,而是抛弃队列里面等待最久的一个任务,然后把拒绝任务加到队列。
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
常见的线程池
- FixedThreadPool,
- CachedThreadPool,
- SingleThreadPool,
- ScheduledThreadPool,
- ForkJoinThreadPool
这些线程池的创建方法是借助于Excutor工具类来创建的,
其不同之处就是线程池的构造方法的参数不同而已
关联文章:
OkHttp的主线流程详解
关注公众号会不定期更新内容:
网友评论