目录
为什么使用线程池
线程池的执行流程
具体实现
核心参数
execute
submit
worker
addWorker
runWorker
getTask方法
拒绝策略
4种拒绝策略
自定义一种拒绝策略
为什么使用线程池
节省频繁创建和回收线程的开销,创建一个线程需要在栈区新建一个栈,需要申请相关的资源,比较耗时。
便于管理线程,可以复用线程,提交任务可以立即执行。
线程池的执行流程
具体实现
核心参数
在线程池的源码中,ctl是一个比较重要的参数,是一个 AtomicInteger,一共32位,高三位用来表示线程池的状态,低29位表示线程池中线程的数量。
execute
执行的流程和上文中的流程图是一致的,其中有比较重要的类和方法。
Worker addWorker
public void execute(Runnable command) {
if (command == null) // 非空判断
throw new NullPointerException();
int c = ctl.get();// 拿到 ctl
if (workerCountOf(c) < corePoolSize) { // 当前池中的线程数小于 核心线程数
if (addWorker(command, true)) // 添加一个核心线程
return;// 添加成功 直接返回
c = ctl.get();// 并发情况下 如果添加核心线程失败 重新获取ctl
}
// 如果创建核心线程 失败
// 首先判断 线程池的状态 是不是 running 并将当前任务 交到任务队列中
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 如果不是 running 状态 那么线程池的状态就是 stop或者shutdown
// 此时 不接受任务 所以 就remove 掉当前的任务 并且执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
// 工作线程为0 但还有任务 在队列中
// 所以添加一个空任务的非核心线程 用来处理队列中的任务
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 如果队列已满 就创建一个救急线程
else if (!addWorker(command, false))
// 创建救急线程失败 执行拒绝策略
reject(command);
}
submit
submit 是先生成一个RunnableFuture,然后再调用 execute方法,返回这个future,通过get方法 阻塞获取结果。
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
worker
继承AQS 用来实现线程中断
实现 runnable 接口
里面是有 线程对象 thread 和 runnable
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
final Thread thread;
Runnable firstTask;
// 填充相关的属性
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}
// 省略部分代码
}
addWorker
addWorker主要是 检查当前线程池的状态 如果是running
再判断线程池的数量是否满足要求 如果满足 就CAS添加该worker 并且执行该任务
如果线程池的数量暂时不满足要求就重试
如果线程执行失败,就进入addWorkerFailed方法
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;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;// 判断当前线程池中的线程数量 是否满足要求
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 {
// 新建一个 worker 对象
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
// 加锁 避免在添加 worker到 workers 的时候 出现 shutdown等情况
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();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
// 执行任务
t.start();
workerStarted = true; // 标记状态
}
}
} finally {
// 如果 添加 worker 对象失败 或者没有成功的执行任务
if (! workerStarted)
// 执行补救措施 主要是
// CAS 线程数-1 因为之前加了
// workers中 移除当前的任务
// 尝试终结线程
addWorkerFailed(w);
}
return workerStarted;
}
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();// CAS --
tryTerminate();
} finally {
mainLock.unlock();
}
}
runWorker
执行任务的流程:
传入的任务就直接执行 执行完毕之后 就会 去take 队列中的任务 继续执行
如果队列中没有任务 那么当前 worker 就会在对应的,condition 中await等待,直到有任务来被唤醒
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 传入的任务就直接执行 执行完毕之后 就会 去take 队列中的任务 继续执行
// 如果队列中没有任务 那么当前 worker 就会在对应的condition 中等待
// 直到有任务来
while (task != null || (task = getTask()) != null) {
w.lock();
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方法
拒绝策略
4种拒绝策略
java的线程池目前有4种拒绝策略,如下图:
如果在创建线程池的时候没有指定拒绝策略,默认会采用 AbortPolicy。
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
1. AbortPolicy:直接丢弃任务,抛出异常,这是默认策略
2. CallerRunsPolicy:只⽤调⽤者所在的线程来处理任务
3. DiscardOldestPolicy:丢弃等待队列中最旧的任务,并执⾏当前任务
4. DiscardPolicy:直接丢弃任务,也不抛出异常
源码如下,都实现了RejectedExecutionHandler 接口,是策略模式的一种体现。
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
// 线程池没有被shutdown的情况下 就让caller去 run当前这个任务,
//caller 是线程池的调用者
// 如果线程池被shutdown 了,那么当前任务就会被抛弃。
if (!e.isShutdown()) {
r.run();
}
}
}
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());
}
}
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
// 直接抛弃任务 没有任何信息输出
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
// 线程池没有被shutdown的情况下 就抛弃队列中 等的最久的任务(队头),空出位置给当前任务
//(多线程下有可能失败,会不断的调用相应的拒绝策略直到成功)
// 如果线程池被shutdown 了,那么当前任务就会被抛弃。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
自定义一种拒绝策略
相当于把一个接口组合了进来,我们可以重写该接口的方法,自定义一种拒绝策略。
比如: