线程池源码系列
一、接口层次
- Executor
- ExecutorService
- AbstractExecutorService
- ThreadPoolExecutor
- ForkJoinPool
- ScheduleExecutorService
- ScheduleThreadPoolExecutor
- Executors 线程池工具类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yzg9c7Nr-1633849989833)(.ThreadPoolExecutor.assets1624024421050.png)]
二、Executor
public interface Executor {
// 执行任务方法
void execute(Runnable command);
}
三、ExecutorSerivce
对父接口进行了功能扩展
- 控制线程池的操作
- 提交任务的操作
- 支持有返回值的任务
- 执行多个和任意任务的操作
public interface ExecutorService extends Executor {
// 关闭线程池
void shutdown();
// 立即关闭线程池
List<Runnable> shutdownNow();
// 线程池是否已关闭
boolean isShutdown();
// 线程池是否已销毁
boolean isTerminated();
// 等待线程池销毁 timout + unit
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
// 提交任务的方法 Callable 有返回值的任务
<T> Future<T> submit(Callable<T> task);
// 提交任务的方法 Runnable + result 也支持返回值
<T> Future<T> submit(Runnable task, T result);
// 提交 Runnable 无返回值
Future<?> submit(Runnable task);
// 执行所有的任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
// 执行所有的任务 + 超时时间限制
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
// 执行任意一个任务
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
// 执行任意一个任务 + 超时时间限制
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
四、AbstractExecutorService
实现基本的方法
- 提供模板方法
- 具体的执行方法逻辑
1、创建任务 RunnableFuture 任务 -> FutureTask
// 接受 Runnable 任务 + T 的返回值类型
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
// 接受 Callable 任务(自带返回值)
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
2、提交任务
// 提交 Runnable 任务
public Future<?> submit(Runnable task) {
// 基本判空
if (task == null) throw new NullPointerException();
// 创建任务 FutureTask
RunnableFuture<Void> ftask = newTaskFor(task, null);
// 执行任务
execute(ftask);
// 返回 FutureTask
return ftask;
}
// 提交 Runnable + T 返回值的任务
public <T> Future<T> submit(Runnable task, T result) {
// 基本判空
if (task == null) throw new NullPointerException();
// 创建任务 FutureTask
RunnableFuture<T> ftask = newTaskFor(task, result);
// 执行任务
execute(ftask);
// 返回 FutureTask
return ftask;
}
// 提交 Callable 任务
public <T> Future<T> submit(Callable<T> task) {
// 基本判空
if (task == null) throw new NullPointerException();
// 创建任务 FutureTask
RunnableFuture<T> ftask = newTaskFor(task);
// 执行任务
execute(ftask);
// 返回 FutureTask
return ftask;
}
3、执行任务 - 执行全部任务
// 不带超时时间的执行所有任务 - 一定要执行完全部,除非出现异常
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
// 基本判空
if (tasks == null)
throw new NullPointerException();
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
// 标志位 - 是否全部完成
boolean done = false;
try {
// 循环创建任务,并将任务执行,获取到异步执行的任务 futures
for (Callable<T> t : tasks) {
RunnableFuture<T> f = newTaskFor(t);
futures.add(f);
execute(f);
}
// 循环等待所有任务执行完成
for (int i = 0, size = futures.size(); i < size; i++) {
Future<T> f = futures.get(i);
// 当前任务没有完成就调用 get 阻塞等待
if (!f.isDone()) {
try {
f.get();
// 这里如果我们的任务出现异常,那么会抛出,程序将中断 -> 跳转到 finally 块
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
}
}
}
// 所有任务正常执行完成设置标志位 true,表示全部执行完成
done = true;
// 返回我们的结果
return futures;
} finally {
// 如果没有被设置 true,表示任务没有完全被执行完,也即出现了异常情况
if (!done)
// 拿到任务,依次取消掉任务,当然已经完成的任务取消是没有效果的
// 详情见 FutureTask
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
4、执行任务 - 带超时时间执行全部任务
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException {
// 基本判空
if (tasks == null)
throw new NullPointerException();
// 时间转换为 纳秒 单位
long nanos = unit.toNanos(timeout);
// 异步执行的任务集合
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
// 一样的全部任务完成的标准位
boolean done = false;
try {
// 为了不影响计时出现偏差,这里先把所有的任务创建好
for (Callable<T> t : tasks)
futures.add(newTaskFor(t));
// 截至时间
final long deadline = System.nanoTime() + nanos;
final int size = futures.size();
// 执行任务
for (int i = 0; i < size; i++) {
execute((Runnable)futures.get(i));
nanos = deadline - System.nanoTime();
// 检查是否超时,超时直接返回,这里返回的任务就有可能没有执行的任务
if (nanos <= 0L)
return futures;
}
// 循环等待任务执行完成
for (int i = 0; i < size; i++) {
Future<T> f = futures.get(i);
// 任务没有完成
if (!f.isDone()) {
// 再次超时检查,超时直接退出
if (nanos <= 0L)
return futures;
try {
// 超时阻塞等待
f.get(nanos, TimeUnit.NANOSECONDS);
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
} catch (TimeoutException toe) {
// 超时异常直接返回异步执行的任务 - 存在未完成的异步任务
return futures;
}
nanos = deadline - System.nanoTime();
}
}
// 全部执行完成,返回异步执行的任务 - 全部都已经执行完成的
done = true;
return futures;
} finally {
// 同理,没有全部执行完成-超时或出现了异常
if (!done)
// 依次取消任务 - 已完成的不会有影响
// 详情见 FutureTask
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
5、执行任务 - 执行任意一个任务
// 执行任意一个任务
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
try {
return doInvokeAny(tasks, false, 0);
} catch (TimeoutException cannotHappen) {
assert false;
return null;
}
}
// 带超时时间的执行任意一个任务
public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return doInvokeAny(tasks, true, unit.toNanos(timeout));
}
// 具体的执行方法
private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,
boolean timed, long nanos)
throws InterruptedException, ExecutionException, TimeoutException {
// 基本判空
if (tasks == null)
throw new NullPointerException();
// 任务大小
int ntasks = tasks.size();
// 任务数为0的情况,抛异常
if (ntasks == 0)
throw new IllegalArgumentException();
// 异步执行的任务集合
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks);
// ExecutorCompletionService -> 将当前线程池 this 传入其内部
ExecutorCompletionService<T> ecs =
new ExecutorCompletionService<T>(this);
try {
// 记录异常
ExecutionException ee = null;
// 超时时间
final long deadline = timed ? System.nanoTime() + nanos : 0L;
// 拿到任务迭代器
Iterator<? extends Callable<T>> it = tasks.iterator();
// next() 拿到第一个任务丢给 ExecutorCompletionService,内部对任务进行二次封装
futures.add(ecs.submit(it.next()));
// 任务数量 -1
--ntasks;
// 正在执行的任务数量 +1
int active = 1;
// 死循环
for (;;) {
// 任务执行完成时或任务取消时,会将任务丢到 ExecutorCompletionService 的队列中
// 如果任务没有完成,那么 poll()方法返回的就是 null
Future<T> f = ecs.poll();
// 这里表示任务没有完成
if (f == null) {
// 还有任务
if (ntasks > 0) {
// 任务数量 -1
--ntasks;
// 拿到下一个任务继续
futures.add(ecs.submit(it.next()));
// 正在执行的任务数量 +1
++active;
}
// 如果正在执行的任务为 0 了,表示执行完了,执行 break退出循环
else if (active == 0)
break;
// 如果是带超时的情况,检查是否超时
else if (timed) {
// 超时获取执行结果
f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
// 没有拿到结果,表示超时了
if (f == null)
throw new TimeoutException();
nanos = deadline - System.nanoTime();
}
// 如果其他情况都不满足 - take 方法获取会抛异常
else
f = ecs.take();
}
// 如果有执行完成的任务
if (f != null) {
// 正在执行的任务数量 -1
--active;
try {
// 拿到执行的结果返回
return f.get();
} catch (ExecutionException eex) {
ee = eex;
} catch (RuntimeException rex) {
ee = new ExecutionException(rex);
}
}
}
if (ee == null)
ee = new ExecutionException();
throw ee;
} finally {
// 取消其他没有执行完成的任务,已完成的任务没有影响
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
五、ThreadPoolExecutor
0、Why
1. 传统方式
- 传统使用多线程方式
- 继承 Thread,重写 run 方法
- 实现 Runnable 重写 run 方法
- 实现 Callable 重写 call 方法
- 传统方式的弊端
- 线程是宝贵的系统资源,频繁创建,执行完就销毁,浪费系统资源
- 线程数量不可控,来一个任务就创建一个线程
- 不支持定时、定期任务执行
2. 线程池方式
- 线程池优点
- 管理线程,线程数量可控,线程重用,提升性能
- 支持任务挤压 - 阻塞队列存放任务
- 支持定时执行、周期执行任务
- 线程池要求
- 使用者需要对线程池运行原理有足够的了解
- 使用者需要了解不同线程池的应用场景及其正确使用方式
1、How to use
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor =
new ThreadPoolExecutor( 2,
5,
60,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3));
// 提交 8 个任务
for (int i = 0; i < 8; i++) {
executor.submit(() -> {
System.out.println(Thread.currentThread().getName());
});
}
executor.shutdown();
executor.awaitTermination(100,TimeUnit.SECONDS);
}
2、线程池组件推理
- 线程容器 - 存放线程 HashSet workers
- 核心线程 - corePoolSize
- 临时线程 - (maximumPoolSize - corePoolSize)
- 临时线程存活时间 - long keepAliveTime
- 时间单位 - TimeUnit unit
- 任务队列 - 存放任务 - BlockingQueue workQueue
- 线程工厂 - 创建线程 - ThreadFactory threadFactory
- 拒绝策略 - 任务满了的解决方案 - RejectedExecutionHandler handler
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
3、线程池工作原理
- 第一次提交任务到线程池,创建核心线程处理任务,直到线程数量达到 corePoolSize 核心线程数
- 持续提交任务到线程池,此时将任务存放到任务队列 workQueue 中 ,直到任务队列满了
- 继续提交任务,此时将创建临时线程任务处理,直到线程数量达到 maximumPoolSize 最大线程数
- 此时任务队列满了、线程数达到最大值了如果继续提交任务,那么线程池将启用拒绝策略来拒绝任务,默认提供了四种拒绝策略
- 随着时间的推移,任务越来越少,那么线程池将根据设置的 keepAliveTime 、unit 进行销毁线程,直到销毁的线程数量达到 corePoolSize 为止
4、线程池工厂
public interface ThreadFactory {
// 创建线程
Thread newThread(Runnable r);
}
5、四大拒绝策略 - RejectedExecutionHandler
// ① 直接抛异常
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 CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
// ③ 抛弃当前提交的任务
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
// ④ 抛弃最早提交的任务
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
// 任务队列队头任务弹出抛弃
e.getQueue().poll();
// 尝试执行新任务
e.execute(r);
}
}
}
6、阻塞队列 BlockingQueue - 多线程安全
// 增删改查
public interface BlockingQueue<E> extends Queue<E> {
boolean add(E e);
boolean offer(E e);
void put(E e) throws InterruptedException;
boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException;
E take() throws InterruptedException;
E poll(long timeout, TimeUnit unit)
throws InterruptedException;
int remainingCapacity();
boolean remove(Object o);
public boolean contains(Object o);
int drainTo(Collection<? super E> c);
int drainTo(Collection<? super E> c, int maxElements);
}
- ArrayBlockingQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- SynchronousQueue
7、FutureTask
public interface Future<V> {
// 取消任务
boolean cancel(boolean mayInterruptIfRunning);
// 是否已取消
boolean isCancelled();
// 是否完成
boolean isDone();
// 获取结果
V get() throws InterruptedException, ExecutionException;
// 超时获取结果
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
public class FutureTask<V> implements RunnableFuture<V> {
}
8、源码解读
1.源码分析
- 向ThreadPool提交任务 - execute()
- 创建新线程 - addWorker(Runnable firstTask, boolean core)
- 线程的主循环 - Worker.runWorker(Worker w)
- 从队列中获取排队的任务 - getTask()
- 线程结束 - processWorkExit(Worker w, boolean completedAbruptyly)
- shutdown()、shutdownNow()、tryTerminated()
计数通用逻辑:先+1,再处理,若处理失败再-1
2.变量
// ctl = 线程数量 + 线程池状态(高3位表示线程池状态,低29位表示线程池线程数量)
// 默认值 0
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 29 位
private static final int COUNT_BITS = Integer.SIZE - 3;
// 向左移动29位,30位为1,其余为0,减1,则为 29个1
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 线程池运行状态
// ctl < 0 时代表运行状态
private static final int RUNNING = -1 << COUNT_BITS; // 111
private static final int SHUTDOWN = 0 << COUNT_BITS; // 000
private static final int STOP = 1 << COUNT_BITS; // 001
private static final int TIDYING = 2 << COUNT_BITS; // 010 过度状态
private static final int TERMINATED = 3 << COUNT_BITS; // 011
// 获取当前线程池运行状态 ~CAPACITY = 1110 0000 0000... :前面3个1,后面29个0
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 获取当前线程池的线程数量 CAPACITY = 0001 1111 1111... :前面3个0,后面29个1
private static int workerCountOf(int c) { return c & CAPACITY; }
// 获取 ctl 的值
private static int ctlOf(int rs, int wc) { return rs | wc; }
// 任务队列,存放任务
private final BlockingQueue<Runnable> workQueue;
// Worker -> Runnable workers 存放任务线程的集合
private final HashSet<Worker> workers = new HashSet<Worker>();
3.线程池状态
ThreadPool 线程池的5种状态:
- RUNNING :接收新任务和进程队列任务
- SHUTDOWN :不接收新任务,但是接收进程队列任务
- STOP :不接收新任务也不接收进程队列任务,并且中断正在执行中的任务
- TIDYING :所有任务终止,线程数量为0,线程池转为 TIDING,将会执行 terminated 钩子方法
- TERMINATED: terminated()执行完成
RUNNING: Accept new tasks and process queued tasks SHUTDOWN: Don't accept new tasks, but process queued tasks STOP: Don't accept new tasks, don't process queued tasks and interrupt in-progress tasks TIDYING: All tasks have terminated, workerCount is zero, the thread transitioning to state TIDYING will run the terminated() hook method TERMINATED: terminated() has completed
状态之间的转换:
- RUNNING -> SHUTDOWN:调用 shutdown() 方法
- (RUNNING/SHUTDOWN) -> STOP :调用shutdownNow()方法
- SHUTDOWN -> TIDING :队列和线程池都是空的
- STOP -> TIDING :线程池为空
- TIDING -> TERMINATED :钩子函数 terminated()执行完成
4.构造器
// 构造器开始
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
// 基本变量赋值
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
5.execute(…)
public void execute(Runnable command) {
// 基本判空
if (command == null)
throw new NullPointerException();
// 获取 ctl 值:线程池状态及线程数
int c = ctl.get();
// ① 线程池线程数量小于核心线程数,添加核心线程
if (workerCountOf(c) < corePoolSize) {
// 提交任务,true表示核心线程,添加成功直接返回
if (addWorker(command, true))
return;
// 再次获取ctl,以防另外的线程对线程池进行状态修改
c = ctl.get();
}
// ② 添加任务失败了,核心线程数满了,检查线程池处于运行状态,往队列添加任务
if (isRunning(c) && workQueue.offer(command)) {
// 任务队列添加任务成功,再次获取 ctl
int recheck = ctl.get();
// 检查是否还在运行状态,如果没有在运行了,remove 掉当前添加进去的任务
if (! isRunning(recheck) && remove(command))
// 并拒绝接收当前任务
reject(command);
// 线程池线程数量为 0 了
else if (workerCountOf(recheck) == 0)
// 添加非核心线程来执行任务 false表示非核心线程,null表示补充线程
addWorker(null, false);
}
// ③ 队列满了,添加非核心线程执行任务
else if (!addWorker(command, false))
// ④ 如果添加失败,拒绝当前任务
reject(command);
}
6.addWorker(…)
// 创建线程并执行任务,core为 true表示创建核心线程,false表示创建临时线程
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
// 获取 ctl 值
int c = ctl.get();
// 线程池运行状态
int rs = runStateOf(c);
// rs >= SHUTDOWN = SHUTDOWN/STOP/TIDING/TERMINATED
// rs == SHUTDOWN 不可以接收外部任务
// first == null 外部提交任务为空
// ! workQueue.isEmpty() 任务队列不为空
// ① 一言以蔽之:线程池处于关闭状态,还有任务提交进来,直接返回 false 表示添加失败
// firstTask == null 表示是添加补充线程来执行队列中的任务addWorker(null, false);
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
// 这个循环就是添加线程数+1
for (;;) {
// 获取线程池线程数量
int wc = workerCountOf(c);
// ② 线程数大于最大容量
if (wc >= CAPACITY ||
// ③ 创建核心线程时线程数超过了corepoolSize,创建非核心线程数超过了最大线程数时
wc >= (core ? corePoolSize : maximumPoolSize))
// 直接返回 false 表示失败
return false;
// ④ CAS线程数+1,成功直接跳过大循环往后执行
if (compareAndIncrementWorkerCount(c))
// 跳过大循环,跳到双层循环下面的代码执行
break retry;
// ⑤ CAS添加线程数失败了
// 再次获取 ctl 值
c = ctl.get();
// 查看当前线程池运行状态和前面的状态是否一致,不一致则重新从外层循环开始
if (runStateOf(c) != rs)
// 退出当前循环,跳到外层循环继续下一次循环
continue retry;
// 一致则继续在内部循环运行
}
}
// 到这里表示上面已经添加线程数成功了
boolean workerStarted = false; // 任务已启动标志位
boolean workerAdded = false; // 任务已添加标志位
Worker w = null;
try {
// ① 包装任务,并创建线程 - 设置 AQS state = -1,禁止中断,直到 runWorker
w = new Worker(firstTask);
// 拿到线程
final Thread t = w.thread;
if (t != null) {
// 可重入锁,加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 拿到线程池运行状态
int rs = runStateOf(ctl.get());
// rs < SHUTDOWN = RUNNING 线程池运行中
if (rs < SHUTDOWN ||
// 补充线程
(rs == SHUTDOWN && firstTask == null)) {
// 线程已启动了,抛异常
if (t.isAlive())
throw new IllegalThreadStateException();
// ② 添加包装后的任务到 HashSet集合中
workers.add(w);
// 获取集合大小,并将此时的值赋值给 largestPoolSize
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
// 修改已添加标志位
workerAdded = true;
}
} finally {
// 解锁
mainLock.unlock();
}
// ③ 任务添加成功,启动线程
if (workerAdded) {
t.start();
// 修改已启动标志位
workerStarted = true;
}
}
} finally {
// ④ 没有启动成功
if (! workerStarted)
// 移除任务,并将线程数-1
addWorkerFailed(w);
}
// 返回是否启动成功结果
return workerStarted;
}
7.runWorker(…)
final void runWorker(Worker w) {
// 当前worker线程
Thread wt = Thread.currentThread();
// 当前worker线程的任务
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // addWorker时设置 -1禁止中断,到这里允许中断 - 设置 AQS state = 0
boolean completedAbruptly = true;
try {
// task != null 表示是第一个任务
// task == null 执行task = getTask() 从任务队列拿任务
// ① 一言以蔽之:任务不为 null 的情况
while (task != null || (task = getTask()) != null) {
// 设置状态位 - 表示此线程为非空闲线程
w.lock();
// ② runStateAtLeast(ctl.get(), STOP) => rs >= STOP 线程池处于关闭状态
if ((runStateAtLeast(ctl.get(), STOP) ||
// 清空线程中断标志位
(Thread.interrupted() &&
// 再次判断 rs >= STOP 线程池处于关闭状态
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 {
// FOR GC
task = null;
// 完成任务数+1
w.completedTasks++;
// 任务执行完成,设置线程标志位 - 表示当前线程空闲
w.unlock();
}
}
// getTask() 内部catch了异常,因此只有2个钩子的方法会导致 completedAbruptly=true
// beforeExecute/afterExecute 会导致 completedAbruptly = true,用户导致的异常
completedAbruptly = false;
} finally {
// 根据 completedAbruptly 处理情况不同的逻辑
processWorkerExit(w, completedAbruptly);
}
}
8.getTask()
private Runnable getTask() {
// 超时标志位
boolean timedOut = false;
// 死循环
for (;;) {
// 拿到当前 ctl
int c = ctl.get();
// 线程池运行状态
int rs = runStateOf(c);
// 线程池处于SHUTDOWN状态,不接收外部任务
// 且线程池处于STOP状态,不接受外部任务和队列任务,或者任务队列为空
// ① 一言以蔽之:没有任务了
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 线程数-1
decrementWorkerCount();
return null;
}
// 拿到线程数
int wc = workerCountOf(c);
// 允许核心线程超时销毁或者线程数大于核心线程数
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 线程数大于最大线程数且超时了
if ((wc > maximumPoolSize || (timed && timedOut))
// 线程数大于1,或任务队列空了
&& (wc > 1 || workQueue.isEmpty())) {
// CAS线程数-1
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// timed 一般是 false 线程数大于了核心线程,带时间poll等待任务
// 否则阻塞等待任务 take()
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
// 拿到任务就返回
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
9.processWorkerExit(…)
// 线程池线程退出情况处理 - 用户导致异常导致线程池线程退出,需要给线程池补充线程
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// ① 用户导致了异常,线程池线程数-1
if (completedAbruptly)
decrementWorkerCount();
// 加锁操作
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
// ② 从 workers 集合中移除当前的 worker
workers.remove(w);
} finally {
// 解锁操作
mainLock.unlock();
}
// 尝试销毁
tryTerminate();
// 获取线程池状态
int c = ctl.get();
// runStateLessThan(c, STOP) = RUNNING/SHUTDOWN
if (runStateLessThan(c, STOP)) {
// ③ 是否用户导致了异常,如果是,跳到addWorker(null, false);补充一个worker线程
// 也即:用户导致了异常,线程池会新创建一个线程
if (!completedAbruptly) {
// 获取核心线程数大小
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
// 核心线程数如果设置为 0 且任务队列还有任务,还需要线程继续执行任务的哟
if (min == 0 && ! workQueue.isEmpty())
// 设置最少还需要一个线程
min = 1;
// ④ 获取线程池当前的线程数和至少的线程数比较,如果大于表示线程池还有线程,直接返回
// 否则跳到 addWorker(null, false); 给线程池补充一个worker线程
if (workerCountOf(c) >= min)
return; // replacement not needed
}
// 给线程池补充一个worker线程
addWorker(null, false);
}
}
10.shutdown()
// SHUTDOWN : 不接收任务,但是执行 workerQueue 中的任务
public void shutdown() {
// 加锁操作
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 检查权限
checkShutdownAccess();
// 修改运行状态改为 SHUTDOWN
advanceRunState(SHUTDOWN);
// 中断空闲的线程
interruptIdleWorkers();
// 钩子方法
onShutdown();
} finally {
// 解锁操作
mainLock.unlock();
}
// TODO - 看后面
tryTerminate();
}
// CAS改变线程池状态
private void advanceRunState(int targetState) {
// 死循环
for (;;) {
// 拿到 ctl
int c = ctl.get();
// 判断线程池是否至少处在了要改变的状态 rs >= targetState,是直接break
// 否则 CAS 改变线程池运行状态,如果失败继续下一次循环
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
// 中断空闲线程
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
private void interruptIdleWorkers(boolean onlyOne) {
// 全局加锁操作
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 循环 workers 集合
for (Worker w : workers) {
// 拿到线程对象
Thread t = w.thread;
// 如果线程未被中断,且 tryLock成功表示空闲线程(前面 w.lock执行任务时设置值,如果在执行任务,tryLock会失败)
if (!t.isInterrupted() && w.tryLock()) {
try {
// 中断线程
t.interrupt();
} catch (SecurityException ignore) {
} finally {
// unlock
w.unlock();
}
}
// 如果只是一个,break
if (onlyOne)
break;
}
} finally {
// 全局锁释放
mainLock.unlock();
}
}
11.shutdownNow()
// STOP : 不接收任务,也不执行 workerQueue 中的任务,可以拿到队列中的任务
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
// 加锁操作
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 检查权限
checkShutdownAccess();
// 修改运行状态为 STOP
advanceRunState(STOP);
// 中断所有的线程
interruptWorkers();
// 取出队列中的任务进行返回
tasks = drainQueue();
} finally {
// 解锁操作
mainLock.unlock();
}
// TODO - 看后面
tryTerminate();
return tasks;
}
// 中断所有的线程
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 循环全部中断
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
void interruptIfStarted() {
Thread t;
// AQS的 state 状态值大于大于0时成立(防止线程池还没有启动就关闭的操作)
// 线程不为null,且线程没有被中断过
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
// 中断线程
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
// 获取 workQueue 队列中的任务
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> q = workQueue;
ArrayList<Runnable> taskList = new ArrayList<Runnable>();
// 这里是实现了drainTo方法的情况,取出队列的任务添加到list并移除任务
q.drainTo(taskList);
// 子类可能没有实现drainTo方法,所以需要以下的操作
if (!q.isEmpty()) {
// 取出 workQueue 队列中的任务进行移除并添加到 list集合中进行返回
for (Runnable r : q.toArray(new Runnable[0])) {
if (q.remove(r))
taskList.add(r);
}
}
return taskList;
}
12.awaitTermination(…)
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
// 加锁操作
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (;;) {
// 查看线程池运行状态是否 TERMINATED,是直接返回,表示线程池已经终结
if (runStateAtLeast(ctl.get(), TERMINATED))
return true;
// 看下等待时间,小于等于不等待,直接返回
if (nanos <= 0)
return false;
// 条件等待队列等待被唤醒 termination Condition 条件等待队列
nanos = termination.awaitNanos(nanos);
}
} finally {
// 解锁操作
mainLock.unlock();
}
}
13.tryTerminate()
// 销毁方法
final void tryTerminate() {
for (;;) {
// 当前 ctl
int c = ctl.get();
// 线程池是运行状态- 直接退出
if (isRunning(c) ||
// 线程池处在 TIDING/TERMINATED - 直接退出
runStateAtLeast(c, TIDYING) ||
// 线程池状态处于 SHUTDOWN,此时不接收任务,但执行队列中的任务,判断队列是否为空
// 队列不为空,还有任务要执行 - 直接退出
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
// 线程池线程数不为 0 表示还有其他线程
// 每个线程都会走这里,这里只需要确保一个线程继续往下走即可
if (workerCountOf(c) != 0) {
// 中断空闲线程 - 只中断一个[因为需要执行后续的代码,线程不能全部退出]
interruptIdleWorkers(ONLY_ONE);
return;
}
// 加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// CAS 将线程池运行状态改为 TIDYING
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
// 执行 terminated() 方法
terminated();
} finally {
// CAS 将线程池运行状态改为 TERMINATED
ctl.set(ctlOf(TERMINATED, 0));
// 唤醒 TERMINATED 状态的线程 awaitTerminated
termination.signalAll();
}
return;
}
} finally {
// 解锁
mainLock.unlock();
}
}
}
六、ScheduledThreadPoolExecutor
0、Why
- ThreadPoolExecutor
需要有存入线程的容器:HashSet
需要有存放任务的容器:BlockingQueue
- 由于工作队列是多线程共享,也即线程容器中的线程都需要访问,所以这个队列必须是多线程并发安全的
- 由于工作线程在没有任务可以执行的时候,那么需要阻塞,所以这个队列要支持没有任务的时候让线程阻塞
因此:BlockingQueue 满足
ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor
由于Scheduled线程池需要进行任务调度,且需要延迟执行,那么任务必须能够排队执行
由于放入的任务不一定能够立即执行,所有还需要存入队列,然后获取,看看是否满足执行条件:时间是否满足
因此:
- ThreadPoolExecutor 原有部分逻辑不满足此需求,所以需要继承TPE并且增加自己的执行逻辑
- 由于任务需要排队且时间排序,那么需要实现自己的 BlockingQueue:DelayedWorkQueue
1、How to use
public static void main(String[] args) throws InterruptedException {
ScheduledExecutorService scheduledExecutorService =
new ScheduledThreadPoolExecutor(2);
// 延迟执行1次
scheduledExecutorService.schedule(() ->
System.out.println(new Date()),2,TimeUnit.SECONDS);
/**
* Sat Jun 19 15:48:47 CST 2021
* Sat Jun 19 15:48:49 CST 2021
* Sat Jun 19 15:48:51 CST 2021
*/
// 周期执行:任务开始时开始计时
scheduledExecutorService.scheduleAtFixedRate(() -> {
System.out.println(new Date());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
},0,2,TimeUnit.SECONDS);
/**
* Sat Jun 19 15:48:03 CST 2021
* Sat Jun 19 15:48:07 CST 2021
* Sat Jun 19 15:48:11 CST 2021
*/
// 周期执行:任务结束时开始计时
scheduledExecutorService.scheduleWithFixedDelay(() -> {
System.out.println(new Date());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
},0,2,TimeUnit.SECONDS);
}
2、ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor,主要用来在给定的延迟之后运行任务,或者定期执行任务
- 使用自定义任务类型 ScheduledFutureTask ,即使不需要调度的任务也会被认为是延时为0的延时任务
- 使用自定义的队列 DelayedWorkQueue,DelayQueue 是无界队列,与 ThreadPoolExecutor 比较缺少容量的限制、corePoolSize 、maximumPoolSize,有效的简化了一些执行机制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BOMqU0go-1633849992233)(.1624343877268.png)]
3、ScheduledFutureTask 任务对象
// 比较器
public interface Delayed extends Comparable<Delayed> {
// 获取延迟时间
long getDelay(TimeUnit unit);
}
public interface ScheduledFuture<V> extends Delayed, Future<V> {
}
public interface RunnableScheduledFuture<V> extends RunnableFuture<V>, ScheduledFuture<V> {
// 是否周期性执行
boolean isPeriodic();
}
private class ScheduledFutureTask<V>
extends FutureTask<V> implements RunnableScheduledFuture<V> {
// 任务序列号
private final long sequenceNumber;
// 任务执行的时间 - 单位毫秒
private long time;
// 重复执行任务的周期,正数表示固定周期执行,负数表示固定延时执行,0表示不是周期任务
private final long period;
// 任务包装
RunnableScheduledFuture<V> outerTask = this;
// 延时队列的索引号
int heapIndex;
}
主要方法:
- 任务对象大小比较 - compareTo(Delayed other)
- run()
- cancel()
构造器:
// 构造定时任务,在 ns 纳秒后执行,执行完成后结果为 result ScheduledFutureTask(Runnable r, V result, long ns) { super(r, result); this.time = ns; this.period = 0; this.sequenceNumber = sequencer.getAndIncrement(); } // 构造一个周期任务,在 ns 纳秒后执行,每隔 period 纳秒执行一次 ScheduledFutureTask(Runnable r, V result, long ns, long period) { super(r, result); this.time = ns; this.period = period; this.sequenceNumber = sequencer.getAndIncrement(); } // 构造一个 Callable 定时任务,在 ns 纳秒后执行 ScheduledFutureTask(Callable<V> callable, long ns) { super(callable); this.time = ns; this.period = 0; this.sequenceNumber = sequencer.getAndIncrement(); }
4、接口方法
public interface ScheduledExecutorService extends ExecutorService {
// 延迟 delay 时间执行任务
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
// 延迟 delay 时间执行任务
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
// 固定频率周期执行任务 - 任务开始时开始计时
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
// 固定延时周期执行任务 - 任务结束时开始计时
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
6、源码分析 - 几个主要的方法
1.构造器
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
// 无界延迟队列
new DelayedWorkQueue());
}
2.schedule(…)
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}
3.take()
// 任务获取
public RunnableScheduledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
// 任务数组获取任务 - 小顶堆
RunnableScheduledFuture<?> first = queue[0];
// 任务不存在
if (first == null)
// 线程等待
available.await();
// 任务存在
else {
// 任务延时时间
long delay = first.getDelay(NANOSECONDS);
// 已经超时
if (delay <= 0)
// 弹出任务 并根据延时时间大小调整堆
return finishPoll(first);
// 任务还没到时间
first = null;
// 任务线程不为空,去等待
if (leader != null)
available.await();
else {
// 任务线程为空,设置任务线程为当前线程
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
// 延时等待
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
// 任务线程为空,且有任务,唤醒等待线程
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}
4.scheduleAtFixedRate(…)
// 固定频率周期任务 - 任务开始时计时
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
// 基本判空
if (command == null || unit == null)
throw new NullPointerException();
// 大于0 表示固定频率执行任务,小于0 表示固定延时任务,等于0 表示非周期任务,不合法
if (period <= 0)
throw new IllegalArgumentException();
// 任务封装
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
// 延时执行任务
delayedExecute(t);
return t;
}
// 延时执行任务
private void delayedExecute(RunnableScheduledFuture<?> task) {
// 线程池关闭了,拒绝任务
if (isShutdown())
reject(task);
else {
// 添加任务到队列
super.getQueue().add(task);
// 线程池关闭了
if (isShutdown() &&
// 且周期任务不能马上执行
!canRunInCurrentRunState(task.isPeriodic()) &&
// 从队列移除任务成功
remove(task))
// 取消任务
task.cancel(false);
else
// 确保线程预先启动
ensurePrestart();
}
}
// 添加任务线程
void ensurePrestart() {
// 线程数+1
int wc = workerCountOf(ctl.get());
// 线程数小于核心线程数,添加核心线程
if (wc < corePoolSize)
addWorker(null, true);
else if (wc == 0)
// 添加非核心线程
addWorker(null, false);
}
5.scheduleWithFixedDelay(…)
// 固定延时周期任务 - 任务执行完成后开始计时 delay 周期执行
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit) {
// 基本判空
if (command == null || unit == null)
throw new NullPointerException();
// 延时时间 <= 0 不合法
if (delay <= 0)
throw new IllegalArgumentException();
// 任务封装
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(-delay));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
// 延时执行
delayedExecute(t);
return t;
}
6.ScheduledThreadPoolExecutor.run()
// 执行体
public void run() {
// period != 0 是否周期任务,true表示周期性延迟任务,false表示一次性延迟任务
boolean periodic = isPeriodic();
// 是否可以运行延迟任务,返回true表示可以,false表示不行,取反后,表示如果不能运行则取消任务
if (!canRunInCurrentRunState(periodic))
// 取消任务
cancel(false);
// 一次性任务直接执行
else if (!periodic)
ScheduledFutureTask.super.run();
// 周期任务:runAndReset 执行包装后的任务,调用 call 方法
else if (ScheduledFutureTask.super.runAndReset()) {
// 设置下一次周期任务执行时间
setNextRunTime();
// 重新将周期任务放入队列
reExecutePeriodic(outerTask);
}
}
// 取消任务
public boolean cancel(boolean mayInterruptIfRunning) {
// 中断任务
boolean cancelled = super.cancel(mayInterruptIfRunning);
// removeOnCancel 默认 false,除非手动设置,表示中断任务的同时将其从队列中移除
if (cancelled && removeOnCancel && heapIndex >= 0)
remove(this);
return cancelled;
}
// 设置下一次周期任务执行时间
private void setNextRunTime() {
// period > 0 固定频率执行 period = 0 一次性任务 period < 0 固定延时执行
long p = period;
// period > 0 加上固定时间
if (p > 0)
time += p;
else
// period < 0
// 设置延迟任务的下一次触发的时间
time = triggerTime(-p);
}
// 重新将周期任务放入队列
void reExecutePeriodic(RunnableScheduledFuture<?> task) {
// 当前线程池运行状态允许
if (canRunInCurrentRunState(true)) {
// 周期任务重新入队
super.getQueue().add(task);
// 再次检查,如果不允许,那么移除当前任务并取消任务
if (!canRunInCurrentRunState(true) && remove(task))
task.cancel(false);
else
ensurePrestart();
}
}
七、ForkJoinPool
0、Why
- 任务分支合并需求:一个大任务分成多个小任务,小任务执行完后依次返回
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Us5fnU7-1633849989836)(.1624344857633.png)]
如果使用 TPE 去实现这个逻辑,需要业务方自己获取 Future做处理,异常麻烦
当线程池的任务,由于线程池满了,这时需要放入到队列中等待,而大任务只能干等待,什么也做不了,此时,这个线程阻塞在这里就是一种浪费
因此:原有线程池满足不了我们的需求,且扩展 TPE 的代码也无济于事,需要构建一个线程池来满足以上需求,以更高效的执行任务
1、How to use
public static void main(String[] args) throws InterruptedException {
ForkJoinPool forkJoinPool = new ForkJoinPool();
forkJoinPool.submit(() -> System.out.println("hello world"));
forkJoinPool.shutdown();
forkJoinPool.awaitTermination(1, TimeUnit.DAYS);
}
2、源码分析
1.整体架构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vhcDpD7g-1633849989838)(.1624430185493.png)]
- 数组下标奇偶性来判断队列是外部提交队列还是工作窃取队列
- 偶数下标:外部提交队列
- 奇数下标:工作窃取队列
- 双端队列
- TOP 头部存入任务
- 取任务与 mode 有关 asyncMode ? FIFO_QUEUE : LIFO_QUEUE
- FIFO_QUEUE - TOP拿取任务
- LIFO_QUEUE - BASE拿取任务 [默认方式]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dnqugMeo-1633849989840)(.1624431782307.png)]
- 队列空和满 (top 和 base 指针只增不减)
- q.base - q.top = 0 队列为空
- q.base - q.top < 0 队列不为空
- a.length > q.top + 1 - q.base 队列没满
2.变量
// ctl : AC + TC
volatile long ctl; // main pool control
// 线程池运行状态
volatile int runState; // lockable status
// 存储并行度parallelism和执行模式[FIFO_QUEUE/LIFO_QUEUE]
// 高16位存储执行模式,低16位存储并行度
final int config; // parallelism, mode
// runState bits: SHUTDOWN must be negative, others arbitrary powers of two
// 线程池运行状态:rs < 0 表示 SHUTDOWN
private static final int RSLOCK = 1;
private static final int RSIGNAL = 1 << 1; // 2
private static final int STARTED = 1 << 2; // 4
private static final int STOP = 1 << 29; // 30 位为1
private static final int TERMINATED = 1 << 30; // 31 位为1
private static final int SHUTDOWN = 1 << 31; // 32 位为1,小于0,不接受外部任务
// 活动线程数 Active counts
private static final int AC_SHIFT = 48;
// 用于对 ctl 高32位高16位+1,也即活动线程数 AC + 1
private static final long AC_UNIT = 0x0001L << AC_SHIFT;
// 用于取 ctl 高32位高16位的值,也即活动线程数 AC
private static final long AC_MASK = 0xffffL << AC_SHIFT;
// 总线程数 Total counts
private static final int TC_SHIFT = 32;
// 用于对 ctl 高32 位低16位+1,也即总线程数 TC + 1
private static final long TC_UNIT = 0x0001L << TC_SHIFT;
// 用于取 ctl 高32位低16位的值,也即总线程数 TC
private static final long TC_MASK = 0xffffL << TC_SHIFT;
private static final long ADD_WORKER = 0x0001L << (TC_SHIFT + 15); // sign
// Mode bits for ForkJoinPool.config and WorkQueue.config
// 用于取执行模式 mode 的值 [FIFO_QUEUE/LIFO_QUEUE]
static final int MODE_MASK = 0xffff << 16; // top half of int
// 执行模式 LIFO_QUEUE,默认值,从 TOP 取任务
static final int LIFO_QUEUE = 0;
// 执行模式 FIFO_QUEUE,int值第17位为1 - 从 BASE 取任务
static final int FIFO_QUEUE = 1 << 16;
3.构造器
// 无参构造器
public ForkJoinPool() {
// MAX_CAP = 0x7fff = 32767 ; 默认最大线程数 = CPU核心数
this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),
defaultForkJoinWorkerThreadFactory, null, false);
}
public ForkJoinPool(int parallelism,
ForkJoinWorkerThreadFactory factory, // 线程工厂
UncaughtExceptionHandler handler, // 线程异常处理器
boolean asyncMode) { // 执行模式:异步/同步。默认 false
this(checkParallelism(parallelism), // 非法参数校验
checkFactory(factory),
handler,
// static final int LIFO_QUEUE = 0;
// static final int FIFO_QUEUE = 1 << 16;
asyncMode ? FIFO_QUEUE : LIFO_QUEUE, // 取任务的方式:base 和 top
"ForkJoinPool-" + nextPoolId() + "-worker-");
checkPermission();
}
// 最终构造器
private ForkJoinPool(int parallelism,
ForkJoinWorkerThreadFactory factory,
UncaughtExceptionHandler handler,
int mode,
String workerNamePrefix) {
this.workerNamePrefix = workerNamePrefix;
this.factory = factory;
this.ueh = handler;
// SMASK = 0x0000 ffff 将 config 的低16位用于存放并行度,高16位存储执行模式
this.config = (parallelism & SMASK) | mode;
// 以并行度64为例 AC 1111111111000000 TC: 1111111111000000
long np = (long)(-parallelism); // offset ctl counts
// 计算 ctl 的值,AC + TC :高32位的值
this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
}
4.提交任务
- submit(…)
// 将任务封装成FJT,因为FJP只执行FJT的任务
public ForkJoinTask<?> submit(Runnable task) {
ForkJoinTask<?> job;
if (task instanceof ForkJoinTask<?>)
job = (ForkJoinTask<?>) task;
else
job = new ForkJoinTask.AdaptedRunnableAction(task);
externalPush(job); // 核心提交任务方法
return job;
}
将任务封装为 FJP 的任务 ForkJoinTask
- externalPush(…)
final void externalPush(ForkJoinTask<?> task) {
WorkQueue[] ws; WorkQueue q; int m;
// 获取随机数 r
int r = ThreadLocalRandom.getProbe();
int rs = runState;
// 表示任务队列数组已被初始化,且找到的队列也已初始化且没有满,将任务放入队列,唤醒线程执行
if ((ws = workQueues) != null && (m = (ws.length - 1)) >= 0 &&
(q = ws[m & r & SQMASK]) != null && r != 0 && rs > 0 &&
U.compareAndSwapInt(q, QLOCK, 0, 1)) {
ForkJoinTask<?>[] a; int am, n, s;
if ((a = q.array) != null &&
(am = a.length - 1) > (n = (s = q.top) - q.base)) {
int j = ((am & s) << ASHIFT) + ABASE;
U.putOrderedObject(a, j, task);
U.putOrderedInt(q, QTOP, s + 1);
U.putIntVolatile(q, QLOCK, 0);
// n<=1 表示队列中最多有一个任务,唤醒线程执行任务
if (n <= 1)
signalWork(ws, q);
return;
}
U.compareAndSwapInt(q, QLOCK, 1, 0);
}
// 提交任务核心方法
externalSubmit(task);
}
生成随机数,先判断任务队列是否初始化了(优化)、没有则继续调用任务提交方法
- externalSubmit(…)
private void externalSubmit(ForkJoinTask<?> task) {
int r; // initialize caller's probe
// 取随机数
if ((r = ThreadLocalRandom.getProbe()) == 0) {
ThreadLocalRandom.localInit();
r = ThreadLocalRandom.getProbe();
}
// 死循环,会执行多次,每次只走一个分支
for (;;) {
WorkQueue[] ws; WorkQueue q; int rs, m, k;
boolean move = false;
// 判断 rs 线程池运行状态是否 SHUTDOWN 了
if ((rs = runState) < 0) {
tryTerminate(false, false);
throw new RejectedExecutionException();
}
// STARTED状态位没有置位,所以需要初始化
// 注意:如果该分支不执行,但是由于||运算符的存在,这里ws和m变量已经初始化
else if ((rs & STARTED) == 0 ||
((ws = workQueues) == null || (m = ws.length - 1) < 0)) {
int ns = 0;
// 加锁
rs = lockRunState();
try {
// 再次判断了一下STARTED状态位
// 因为多线程操作,可能上面判断过后已经被别的线程初始化了,所以没必要再进行CAS
if ((rs & STARTED) == 0) {
U.compareAndSwapObject(this, STEALCOUNTER, null,
new AtomicLong());
// p 是并行度:工作线程的数量
int p = config & SMASK; // ensure at least 4 slots
int n = (p > 1) ? p - 1 : 1; // n 最小为 1
// 这里取传入的数最近的2的次幂,即队列数组保证是2的次幂长度
// 目的是为了使用奇偶下标区分外部提交队列和工作窃取队列
n |= n >>> 1; n |= n >>> 2; n |= n >>> 4;
n |= n >>> 8; n |= n >>> 16; n = (n + 1) << 1;
// 工作队列数组初始化(外部提交队列+工作窃取队列)
workQueues = new WorkQueue[n];
// 初始化成功,那么此时状态修改为STARTED状态
ns = STARTED;
}
} finally {
// 最后释放锁
unlockRunState(rs, (rs & ~RSLOCK) | ns);
}
}
// 在上一个分支执行完毕后,第二次循环将会到达这里。由于咱们的算法是用了一个全局队列:workQueues来存储两钟队列:外部提交队列、工作窃取队列,那么这时应该去找到这个任务放在哪个外部提交队列里面。通过上面获取的随机种子r,来找到应该放在哪里?SQMASK = 1111110,所以由SQMASK的前面的1来限定长度,末尾的0来表明,外部提交队列一定在偶数位
else if ((q = ws[k = r & m & SQMASK]) != null) {
// 由于当前提交队列是外部提交队列,那么一定会有多线程共同操作,那么为了保证并发安全,那么这里需要上锁,也即对当前提交队列进行锁定
if (q.qlock == 0 && U.compareAndSwapInt(q, QLOCK, 0, 1)) {
// 取外部提交队列的保存任务的数组array
ForkJoinTask<?>[] a = q.array;
// 栈顶指针 top
int s = q.top;
boolean submitted = false;
try {
// 当前任务数组已经初始化 且 数组没有满
if ((a != null && a.length > s + 1 - q.base) ||
// 如果前面不成立,也即 数组没有初始化或数组满了
// 那么这里初始化数组或者扩容
(a = q.growArray()) != null) {
// 扩容之后,开始存放任务
int j = (((a.length - 1) & s) << ASHIFT) + ABASE;
U.putOrderedObject(a, j, task); // 放任务
U.putOrderedInt(q, QTOP, s + 1); // 栈顶指针 top+1
// 为什么这里需要怎么写?注意:qtop和q.array没有volatile修饰
submitted = true;
}
} finally {
// qlock是volatile的,由于volatile的特性,这个操作CAS出去,那么qlock线程可见,必然上面的task和qtop可见,且有序。
U.compareAndSwapInt(q, QLOCK, 1, 0);
}
if (submitted) {
// 由于添加成功了,但是,没有工作线程,那么这时通过signalWork,创建工作线程并执行
signalWork(ws, q);
return;
}
}
// 由于当前线程无法获取初始计算的提交队列的锁,那么这时发生了线程竞争,那么设置move标志位,让线程在下一次循环的时候,重新计算随机数,让它寻找另外的队列。
move = true;
}
// 如果找到的这个wq没有被创建,那么创建他,但是,这里的RSLOCK的判断,在于,当没有别的线程持有RSLOCK的时候,才会进入。这是由于RSLOCK主管,runstate,可能有别的线程把状态改了,根本不需要再继续work了
else if (((rs = runState) & RSLOCK) == 0) {
// 创建外部提交队列,由于ForkJoinWorkerThread FJWT为null,所以为外部提交队列
q = new WorkQueue(this, null);
// r为什么保存为hint,r是随机数,通过r找到当前外部提交队列,处于WQS的索引下标
q.hint = r;
// SHARED_QUEUE = 1 << 31;这里就是将整形int的符号位置1,所以为负数,SHARED_QUEUE表明当前队列是共享队列(外部提交队列)。而k为当前wq处于WQS中的索引下标
q.config = k | SHARED_QUEUE;
// 由于当前wq并没有进行扫描任务,所以扫描状态位无效状态INACTIVE
q.scanState = INACTIVE;
// 对wqs上锁操作:就是将上面的队列放入到WQS的偶数位中
rs = lockRunState();
// 确保线程池处于运行状态
// 由于可能两个线程同时进来操作,只有一个线程持有锁,那么只允许一个线程放创建的队列,但是这里需要注意的是:可能会有多个线程创建了WorkQueue,但是只有一个能成功
if (rs > 0 && (ws = workQueues) != null &&
k < ws.length && ws[k] == null)
// 将wq放入全局队列WQS中
ws[k] = q;
// 解锁
unlockRunState(rs, rs & ~RSLOCK);
}
// 发生竞争时,让当前线程选取其他的wq来重试
else
move = true;
if (move)
// 获取下一个不同的随机数,重新进行尝试
r = ThreadLocalRandom.advanceProbe(r);
}
}
初始化任务队列数组,数组大小为2的次幂,并将任务保存到外部提交队列
5.创建工作窃取队列并绑定线程(FJWT)
- signalWork(…)
// 上面的内容,都是初始化,放任务,但是执行线程没有啊,谁来执行这个放入到了外部提交队列中的任务。
final void signalWork(WorkQueue[] ws, WorkQueue q) {
long c; int sp, i; WorkQueue v; Thread p;
// 符号位没有溢出:最高16位为AC,代表了工作活跃的线程数没有达到最大值
while ((c = ctl) < 0L) {
// ctl的低32位代表了INACTIVE数。若此时sp=0,代表了没有空闲线程
if ((sp = (int)c) == 0) {
// ADD_WORKER = 0x0001L << (TC_SHIFT + 15) TC的最高位是不是0,若不是0,那么FJP中的工作线程代表了没有达到最大线程数
if ((c & ADD_WORKER) != 0L)
// 尝试添加工作线程
tryAddWorker(c);
break;
}
// 此时,代表了空闲线程不为null
// 此时FJP的状态为NOT STARTED,TERMINATED
if (ws == null)
break;
// SMASK = 0xffff 取sp的低16位 TERMINATED
if (ws.length <= (i = sp & SMASK))
break;
// ctl低32位的低16位是不是存放了INACTIVE线程在wqs的下标i TERMINATING
if ((v = ws[i]) == null)
break;
// SS_SEQ = 1 << 16; ctl低32位的高16位是不是存放了版本计数 version count INACTIVE= 1 << 31
int vs = (sp + SS_SEQ) & ~INACTIVE;
int d = sp - v.scanState;
// 把获取到的INACTIVE的线程,也即空闲线程唤醒,那么唤醒后,是不是应该对AC + 1(加1操作(c + AC_UNIT)),UC_MASK为高32位1,低32位0,所以(UC_MASK & (c + AC_UNIT))代表了,保留ctl的高32位值,也即AC+1和TC值
long nc = (UC_MASK & (c + AC_UNIT)) |
// SP_MASK为高32位0,低32位1,所以保留了低32位的值,v.stackPred 代表了一个出栈操作,让低32位的低16位更新为唤醒线程的下一个线程
(SP_MASK & v.stackPred);
// 此时的nc就是计算好的下一个ctl,next ctl -> n
if (d == 0 && U.compareAndSwapLong(this, CTL, c, nc)) {// CAS 替换CTL
// 记录版本信息放入scanState,此时为正数
v.scanState = vs;
if ((p = v.parker) != null)
// 唤醒工作线程
U.unpark(p);
break;
}
// 队列为空,直接退出。由于队列是多线程并发的,所以有可能放入其中的任务已经被其他线程获取,所以此时队列为空
if (q != null && q.base == q.top)
break;
}
}
唤醒空闲线程执行提交的任务,如果没有空闲线程则创建线程执行任务
- tryAddWorker(…)
// 尝试添加工作线程
private void tryAddWorker(long c) {
boolean add = false;
do {
// active count +1 活跃线程数加1,此时只保留了高32位的高16位信息
long nc = ((AC_MASK & (c + AC_UNIT)) |
// total count +1 总线程数加1,此时只保留了高32位的低16位信息
(TC_MASK & (c + TC_UNIT)));
// 此时nc为next ctl,也即活跃线程数+1,总线程数+1
// ctl没有被其他线程改变
if (ctl == c) {
int rs, stop;
// 上锁并检查FJP的状态是否为STOP
if ((stop = (rs = lockRunState()) & STOP) == 0)
// 更新ctl的值。add表明是否添加成功
add = U.compareAndSwapLong(this, CTL, c, nc);
// 解锁
unlockRunState(rs, rs & ~RSLOCK);
// 线程池已经停止
if (stop != 0)
break;
// 如果ac和tc添加1成功,也即nc替换成功,那么创建工作线程
if (add) {
// 创建工作线程
createWorker();
break;
}
}
} while (((c = ctl) & ADD_WORKER) != 0L // 没有达到最大线程数
&& (int)c == 0); // 低32位0,有空闲线程
}
活跃线程数+1:AC+1;总线程数+1:TC+1
- createWorker(…)
// 创建工作线程
private boolean createWorker() {
ForkJoinWorkerThreadFactory fac = factory;
Throwable ex = null;
ForkJoinWorkerThread wt = null;
try {
// 从线程工厂中,创建线程 FJWT 绑定工作窃取队列
if (fac != null && (wt = fac.newThread(this)) != null) {
// 并启动线程 - 执行run方法
wt.start();
return true;
}
} catch (Throwable rex) {
ex = rex;
}
// 如果创建出现异常,将ctl前面加的1回滚
deregisterWorker(wt, ex);
return false;
}
真正的创建线程并启动线程,出现异常调用deregisterWorker方法对前面的AC/TC-1
- ForkJoinWorkerThreadFactory 线程工厂
// 默认线程工厂
static final class DefaultForkJoinWorkerThreadFactory
implements ForkJoinWorkerThreadFactory {
public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
return new ForkJoinWorkerThread(pool); // 直接new ForkJoinWorkerThread
}
}
FJP 默认线程工厂
- ForkJoinWorkerThread
// FJWT的构造器
protected ForkJoinWorkerThread(ForkJoinPool pool) {
super("aForkJoinWorkerThread");
// 保存对外的FJP的引用
this.pool = pool;
// 将自己注册到FJP中,其实就是保存到FJP的奇数位中
this.workQueue = pool.registerWorker(this);
}
FJP 的线程封装类 ForkJoinWorkerThread
- registerWorker(…)
// 将自己注册到FJP中
final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
UncaughtExceptionHandler handler;
// FJWT 设置为守护线程
wt.setDaemon(true);
// 如果设置了线程的异常处理器,那么设置
if ((handler = ueh) != null)
wt.setUncaughtExceptionHandler(handler);
// 创建工作线程,注意:创建外部提交队列时: WorkQueue w = new WorkQueue(this, null)
// 这里创建的是工作窃取队列
WorkQueue w = new WorkQueue(this, wt);
// 创建的工作队列w所保存在wqs的索引下标
int i = 0;
// 从config中取设置的工作模式 mode:FIFO、LIFO
int mode = config & MODE_MASK;
// 加锁
int rs = lockRunState();
try {
WorkQueue[] ws; int n;
// 日常判空操作
if ((ws = workQueues) != null && (n = ws.length) > 0) {
// indexSeed = 0,SEED_INCREMENT = 0x9e3779b9 ,大质数减少hash碰撞
int s = indexSeed += SEED_INCREMENT;
// wqs长度-1,用于取模运算
int m = n - 1;
// 找存放w的下标 - 类似取模运算
// 注意:这里 i 的值一定是奇数
i = ((s << 1) | 1) & m;
// 发生了碰撞
if (ws[i] != null) {
int probes = 0;
// 发生碰撞二次寻址 EVENMASK = 0xfffe;
// step保证为偶数:n是2的次幂,n>>>1 高位后全1,&上低位为0的0xfffe必为偶数
int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2;
// (i + step) & m 计算WQS的索引下标:i 一定是奇数,step一定是偶数
// 一个奇数+偶数一定等于奇数,也即找到工作窃取队列所在的槽位
while (ws[i = (i + step) & m] != null) {
// 寻址达到了极限,那么扩容
if (++probes >= n) {
// 扩容容量为2倍
workQueues = ws = Arrays.copyOf(ws, n <<= 1);
m = n - 1;
probes = 0;
}
}
}
// s作为随机数保存在wq的hint中
w.hint = s;
// 此config是WorkQueue的保存索引下标 + 模式
w.config = i | mode;
// scanState为volatile,此时对它进行写操作,ss写成功,上面的变量一定可见,且不会和下面的ws[i]赋值发生重排序。注意这里的scanState就变成了odd,也即奇数,所以要开始扫描获取任务并执行啦
w.scanState = i;
// 放入全局队列中
ws[i] = w;
}
} finally {
// 解锁
unlockRunState(rs, rs & ~RSLOCK);
}
wt.setName(workerNamePrefix.concat(Integer.toString(i >>> 1)));
return w;
}
创建工作窃取队列 new WorkQueue(this, wt) ,绑定线程FJWT,找到全局 workQueues 数组的奇数下标位置,将其w存放到这个位置上,如果发现已经存在了,则重新计算workQueues的槽位奇数位下标位置,如果计算了一轮还没有找到,则表示需要扩容了,扩容为原来的2倍,扩容后再次计算槽位
6. 执行任务
- run()
// FJWT 的run方法,执行体
public void run() {
// 只run一次。array用于存放FJT,那么这里用它作为标识,来确保FJWT只run一次
if (workQueue.array == null) {
Throwable exception = null;
try {
// run之前执行钩子函数
onStart();
// 核心执行方法
pool.runWorker(workQueue);
} catch (Throwable ex) {
exception = ex;
} finally {
try {
// run之后执行钩子函数
onTermination(exception);
} catch (Throwable ex) {
// 如果异常为空,则保存这里的异常,否则异常为上面的catch块异常。其实呢就是一句话:保存最先发生的异常
if (exception == null)
exception = ex;
} finally {
// 线程退出后,进行状态还原
pool.deregisterWorker(this, exception);
}
}
}
}
FJWT的执行体,确保run方法只执行一次,提供钩子方法onStart、onTermination,供开发者进行扩展
- runWorker(…)
// FJP中的真正处理FJWT工作的函数
final void runWorker(WorkQueue w) {
w.growArray(); // 分配array:初始化或扩容
int seed = w.hint; // 取随机数
int r = (seed == 0) ? 1 : seed; // 避免出现0
// 循环获取任务并执行,直到显示退出
for (ForkJoinTask<?> t;;) {
if ((t = scan(w, r)) != null) // 扫描可执行任务 核心扫描方法 scan
w.runTask(t); // 拿到任务之后开始执行
else if (!awaitWork(w, r)) // 如果没有任务可执行,那么awaitWork等待任务执行
break;
// 异或算法,基于上一个随机数r,计算下一个伪随机数
r ^= r << 13; r ^= r >>> 17; r ^= r << 5;
}
}
分配任务数组,循环扫描队列中的任务,有任务就执行,没有任务就等待
- growArray()
// WorkQueue类中用于初始化或者增长array函数
final ForkJoinTask<?>[] growArray() {
ForkJoinTask<?>[] oldA = array;
//oldA存在,那么进行二倍长度扩容,否则size为初始化大小INITIAL_QUEUE_CAPACITY= 1<<13
int size = oldA != null ? oldA.length << 1 : INITIAL_QUEUE_CAPACITY;
// 如果扩容之后,超过最大容量MAXIMUM_QUEUE_CAPACITY = 1<<26;也即64M,抛出异常
if (size > MAXIMUM_QUEUE_CAPACITY)
throw new RejectedExecutionException("Queue capacity exceeded");
int oldMask, t, b;
// 创建新的array数组
ForkJoinTask<?>[] a = array = new ForkJoinTask<?>[size];
// 扩容后,将旧的array中的任务放入到新的数组中
// 面试题:为什么这里手动复制,而不用更快速的复制?System.arraycopy?面的是什么??如何保证数组中的元素的可见性?getObjectVolatile(数组的首地址,偏移量)
if (oldA != null && (oldMask = oldA.length - 1) >= 0 &&
(t = top) - (b = base) > 0) {
int mask = size - 1;
do {
ForkJoinTask<?> x;
// 注意:这里从base引用开始取任务
int oldj = ((b & oldMask) << ASHIFT) + ABASE;
int j = ((b & mask) << ASHIFT) + ABASE;
x = (ForkJoinTask<?>)U.getObjectVolatile(oldA, oldj);// volatile 语义 1
if (x != null &&
// 这里为何使用CAS?因此由于队列是工作窃取队列,可能有别的线程持有old数组引用,正在通过base引用窃取尾部任务
U.compareAndSwapObject(oldA, oldj, x, null))
U.putObjectVolatile(a, j, x); // volatile 语义 2
} while (++b != t); // 循环,直到转移成功
}
return a;
}
任务数组初始化或扩容,扩容的话将旧数组中的数据放入到新数组中
- scan(…)
// FJP中,在FJWT进行扫描获取任务执行。w为当前FJWT所处队列,r为随机数
private ForkJoinTask<?> scan(WorkQueue w, int r) {
WorkQueue[] ws; int m;
// 日常判空
if ((ws = workQueues) != null && (m = ws.length - 1) > 0 && w != null) {
// 保存初始时的扫描状态
int ss = w.scanState;
// 扫描获取任务。origin初始为随机数取模的下标,k初始为origin。
// 由于在扫描过程中,可能有别的线程添加获取等等操作,那么我怎么样让当前FJWT返回呢?不可能一直在这扫描吧,实在是没有任务了,那么必须退出,避免造成性能损耗,那么问题就变为:如何发现当前没有可执行的任务呢?(这叫推理学习,记忆方式),所以采用oldSum和checkSum来判断整个wqs是否处于稳定状态,也即没有别的线程再往里添加任务了,而且经历过一个周期,没有扫描到可执行任务,即可退出。
for (int origin = r & m, k = origin, oldSum = 0, checkSum = 0;;) {
WorkQueue q; ForkJoinTask<?>[] a; ForkJoinTask<?> t;
int b, n; long c;
// 在wqs中找到了一个不为空的队列。那么看看有没有可以获取任务
if ((q = ws[k]) != null) {
// 由于放任务将会操作top指针,初始时,base等于top,每添加一个任务,top加1,所以随着任务的添加,那么此时base落后于top,所以base - top < 0 表明队列有任务
if ((n = (b = q.base) - q.top) < 0 &&
// 队列中的任务数组array不为空,表明有任务可以获取
(a = q.array) != null) {
// 取array索引下标base的偏移地址
long i = (((a.length - 1) & b) << ASHIFT) + ABASE;
if ((t = ((ForkJoinTask<?>)
U.getObjectVolatile(a, i))) != null && // 任务存在
q.base == b) { // base引用没有被改变,也即任务没有被取走
if (ss >= 0) { // 如果扫描状态正常
// CAS取任务
if (U.compareAndSwapObject(a, i, t, null)) {
q.base = b + 1; // 增加base值
if (n < -1) // 队列大于一个任务,那么其他线程赶紧起来干活
signalWork(ws, q);
return t; // 返回获取的任务
}
}
// oldSum未改变之前,才能判断w的扫描状态,如果扫描状态小于0,代表INACITVE,此时需要尝试唤醒空闲线程进行扫描工作
else if (oldSum == 0 &&
w.scanState < 0)
// c最新的ctl值,ws[m & (int)c]栈顶的索引下标,AC_UNIT 用于计算活跃线程数
tryRelease(c = ctl, ws[m & (int)c], AC_UNIT);
}
// 任务不存在,那么判断,如果此时扫描状态处于INACTIVE的话,那么需要重新获取扫描状态,可能别的线程已经将其置为扫描状态
if (ss < 0)
ss = w.scanState;
// 执行到这里,那么只可能是因为线程竞争导致的,所以为了减少竞争,那么重新计算随机数,转移获取任务的wq,复位origin,oldSum、checkSum 方便计算轮回
r ^= r << 1; r ^= r >>> 3; r ^= r << 10;
origin = k = r & m;
oldSum = checkSum = 0;
continue;
}
checkSum += b; // 通过base数值来进行校验和计算
}
// 扫描wqs正好经历过一个周期
if ((k = (k + 1) & m) == origin) {
// 工作线程处于活跃状态,或者状态没有改变,注意:这里进行了ss的状态更新 ss = w.scanState
if ((ss >= 0 || (ss == (ss = w.scanState))) &&
// 旧的oldSum 和 新的 checkSum 比较,同时更新oldSum
oldSum == (oldSum = checkSum)) {
// 总结: stable 稳定态: 扫描状态不变 且 没有线程操作队列
// 工作线程切换为 INACTIVE且队列稳定,所以退出即可
if (ss < 0 || w.qlock < 0)
break;
int ns = ss | INACTIVE; // 将扫描状态设置为INACTIVE
long nc = ((SP_MASK & ns) |
(UC_MASK & ((c = ctl) - AC_UNIT))); // 活跃线程数减1
//CTL低32位就是空闲线程栈的栈顶,workqueue的stackPred就是栈中的空闲线程
w.stackPred = (int)c; // 之前栈顶空闲线程的索引下标+版本号
// 优化到了极致。由于这里是volatile,进行直接赋值将会导致StoreStore和StoreLoad屏障,所以用unsafe类来普通变量赋值,减少性能损耗,而后面的CAS操作后,自然能够保证这里的scanState语义。
U.putInt(w, QSCANSTATE, ns);
// CAS 替换ctl值
if (U.compareAndSwapLong(this, CTL, c, nc))
// 这里又是一个优化点,因为CTL替换成功,必然scanState写成功,那么局部变量直接更新为最新值,而不用再去读scanState变量
ss = ns;
else
// 如果失败了,那么回退到原来的状态,因为CTL没有改变,也即active count没有减1成功,自然scanState需要回退。
w.scanState = ss;
}
checkSum = 0;
}
}
}
return null;
}
扫描全局 workQueues 数组中每个队列中的任务数组 array,找到任务就返回任务,否则返回 null
- runTask(…)
// 执行WorkQueue中执行scan获取到的FJT
final void runTask(ForkJoinTask<?> task) {
// 日常判空
if (task != null) {
// 标记当前wq的工作线程处于执行获取到的任务状态。即标记为偶数。取scanState的高31位。int SCANNING = 1
scanState &= ~SCANNING;
// task是不是当前线程从队列里面获取的(scan),也即将task设置为currentSteal。这里先不要了解FJT的内容,只需要知道运行了,怎么运行的。不需要了解怎么去实现FJT等等,后面再说。
(currentSteal = task).doExec();
// 执行完毕之后释放currentSteal引用。为什么这里这么写?store buffer -> StoreLoad -> volatile语义?写变量时,StoreStore,StoreLoad。那么这里为了保证写入顺序-> putOrderedObject 避免了StoreLoad屏障对性能的损耗。
U.putOrderedObject(this, QCURRENTSTEAL, null);
// 直接翻译:执行本地任务。为何执行本地任务?考虑一个问题:谁能往当前线程的工作队列里放任务?当前线程在执行FJT时往自己队列里放了任务,也只有当前线程才能往array任务数组里放任务。
execLocalTasks();
ForkJoinWorkerThread thread = owner;
// nsteals代表了当前线程总的偷取的任务数量。由于符号限制,所以检查是否发生符号溢出
if (++nsteals < 0)
// 当前线程32位计数值达到饱和,那么将其加到FJP的全局变量的64位计数器中,并且清零计数值 nsteals
transferStealCount(pool);
// 任务执行完成,恢复扫描状态
scanState |= SCANNING;
if (thread != null)
thread.afterTopLevelExec(); // 任务执行完的钩子函数
}
}
// 执行本地任务
final void execLocalTasks() {
int b = base, m, s;
// 当前工作线程的任务数组
ForkJoinTask<?>[] a = array;
if (b - (s = top - 1) <= 0 && // base 和 top 指针距离至少是1,表示至少有一个任务可拿
a != null && // 任务数组不为空
(m = a.length - 1) >= 0) { // 任务数组有任务
// 取任务的模式
if ((config & FIFO_QUEUE) == 0) { // 从 top 拿任务
for (ForkJoinTask<?> t;;) {
if ((t = (ForkJoinTask<?>)U.getAndSetObject
(a, ((m & s) << ASHIFT) + ABASE, null)) == null)
break;
U.putOrderedInt(this, QTOP, s);
t.doExec();
if (base - (s = top - 1) > 0)
break;
}
}
else // FIFO_QUEUE 从 base 拿任务
pollAndExecAll();
}
}
执行任务前修改 scanState,然后执行任务,执行完任务后恢复 scanState
- awaitWork(…)
// FJP用于FJWT工作线程等待唤醒的方法
private boolean awaitWork(WorkQueue w, int r) {
// 线程池正在Terminate
if (w == null || w.qlock < 0)
return false; // 返回false 直接退出runWorker内部循环,也即退出FJWT
// 取当前工作线程压入空闲栈中的前一个工作线程版本号+下标(32位切割为:高16 + 低16)赋值于pred,SPINS代表自旋次数为0
for (int pred = w.stackPred, spins = SPINS, ss;;) {
// 当前工作线程状态已经被修改为ACTIVE状态,那么赶紧干活去
if ((ss = w.scanState) >= 0)
break;
else if (spins > 0) { // 没有达到自旋次数阈值 spins 自旋次数
r ^= r << 6; r ^= r >>> 21; r ^= r << 7;
if (r >= 0 && --spins == 0) { // randomize spins
WorkQueue v; WorkQueue[] ws; int s, j; AtomicLong sc;
if (pred != 0 && (ws = workQueues) != null &&
(j = pred & SMASK) < ws.length &&
(v = ws[j]) != null && // see if pred parking
(v.parker == null || v.scanState >= 0))
spins = SPINS; // continue spinning
}
}
// 自旋之后再次检测下线程池状态
else if (w.qlock < 0)
return false;
// 如果当前FJWT工作线程没有发生中断,那么尝试睡眠,否则清除中断标志位后继续scan扫描任务,干活去
else if (!Thread.interrupted()) {
long c, prevctl, parkTime, deadline;
// 之前我们以并行度64为例 AC(高32位的高16位) 1111 1111 1100 0000 64并行度:0000 0000 0100 0000
// AC_SHIFT = 48 符号右移。获取高16位的值:活跃线程数
// 取低16位config的值:在构造函数中设置的并行度(这里是64)SMASK = 0xffff
int ac = (int)((c = ctl) >> AC_SHIFT) + (config & SMASK);
// 总结:用64位的ctl的高32位的高16位与设置的并行度相加可以得到ac,也即活跃线程数
// 此时 ac = 0 。那么代表了活跃线程数为0。活跃线程数为0之后,我需要看看FJP有没有设置SHUTDOWN或者STOP。此时需要调用tryTerminate看看自己是不是最后一个线程,然后做清理和进一步的操作
if ((ac <= 0 && tryTerminate(false, false)) ||
// 线程池状态处于STOP停止态,所以停止执行
(runState & STOP) != 0)
return false;
// 看看当前线程是不是最后一个空闲线程
if (ac <= 0 && ss == (int)c) {
// 为什么这里要加1?我当前线程结束了吗?在我FJP线程代码没有执行完毕之前我还是属于FJP的,所以还是属于活跃线程。这里TC都还没改呢?
prevctl = (UC_MASK & (c + AC_UNIT)) |
// 因为当前线程要释放资源,不属于FJP了,所以栈顶还原到之前的索引下标
(SP_MASK & pred);
// 取CTL中的总线程数:活跃线程数(AC)+非活跃线程数(空闲线程数)
int t = (short)(c >>> TC_SHIFT);
// 保证至少有三个空闲线程即可。由于当前线程池处于静默状态,如果持有太多空闲线程将会浪费系统资源,那么何不如把线程释放掉,而又需要保持线程池的活性,即有任务也能不创建线程执行,那么这里设置阈值为3个空闲线程数。这里写法不规范,理论上来说这是可以调节的,设置为全局变量比较合适
if (t > 2 &&
// 如果这里t>2,那么表明有足够多的空闲线程,那么我需要将CTL还原到 + 1 ac 和 之前栈顶的线程索引
U.compareAndSwapLong(this, CTL, c, prevctl))
return false;
parkTime = IDLE_TIMEOUT * ((t >= 0) ? 1 : 1 - t);
deadline = System.nanoTime() + parkTime - TIMEOUT_SLOP;
}
else
prevctl = parkTime = deadline = 0L;
Thread wt = Thread.currentThread();
// 模拟 LockSupport。其实就是设置阻塞对象,表示当前线程阻塞在哪个对象上
U.putObject(wt, PARKBLOCKER, this);
// 设置当前workqueue阻塞在哪个线程上
w.parker = wt;
// 在执行阻塞之前,再次检测状态位:当前worker必须处于空闲状态且ctl没有被改变过
if (w.scanState < 0 && ctl == c)
U.park(false, parkTime);
// 被唤醒后,清空标识字段
U.putOrderedObject(w, QPARKER, null);
U.putObject(wt, PARKBLOCKER, null);
// 被唤醒后,扫描状态被设置>0,说明处于活跃状态,直接break
if (w.scanState >= 0)
break;
if (parkTime != 0L && // 设置了阻塞时间
ctl == c && // ctl没有改变过
deadline - System.nanoTime() <= 0L && // 超时了
U.compareAndSwapLong(this, CTL, c, prevctl)) // 收缩线程池
return false;
}
}
return true;
}
自旋等待被唤醒
7. 线程池关闭
- shutdown()
public void shutdown() {
// 检查权限
checkPermission();
tryTerminate(false, true);
}
- tryTerminate(…)
// 实际关闭方法。enable指明如果线程池状态处于活跃状态时,能不能修改状态。
private boolean tryTerminate(boolean now, boolean enable) {
int rs;
// 当前线程池是common公用线程池不允许被关闭
if (this == common)
return false;
if ((rs = runState) >= 0) { // 线程池处于活跃状态
if (!enable) // 如果enable为false,直接退出
return false;
rs = lockRunState(); // 进入 SHUTDOWN 阶段
// 去除RSLOCK位,并加上SHUTDOWN标志位
unlockRunState(rs, (rs & ~RSLOCK) | SHUTDOWN);
}
// 此时标志位SHUTDOWN已经设置,那么通过之前的描述得知,此时线程池不会再接收新的任务
// 此时,线程池状态处于SHUTDOWN状态,没有设置STOP标志位
if ((rs & STOP) == 0) {
// 如果没有设置立即无条件结束线程池,那么需要检测一下静默状态,看看是否所有线程都是空闲的
if (!now) {
// 重复检测,直到FJP稳定
for (long oldSum = 0L;;) {
WorkQueue[] ws; WorkQueue w; int m, b; long c;
// 校验和变量默认等于最新的ctl值
long checkSum = ctl;
// 计算ac值, ac大于0,说明仍然有活跃线程,直接返回即可
if ((int)(checkSum >> AC_SHIFT) + (config & SMASK) > 0)
return false;
// wqs都没有创建,那就直接结束呗
if ((ws = workQueues) == null || (m = ws.length - 1) <= 0)
break;
// 遍历所有队列。包括:外部提交队列、内部工作队列(窃取队列)
for (int i = 0; i <= m; ++i) {
// 队列存在
if ((w = ws[i]) != null) {
if ((b = w.base) != w.top || // 队列中有任务
w.scanState >= 0 || // 处于工作状态
w.currentSteal != null) {// 正在执行任务
// 唤醒INACTIVE线程,尽快完成工作
tryRelease(c = ctl, ws[m & (int)c], AC_UNIT);
return false; // 当前FJP还有任务在执行,当前线程先返回
}
// 计算校验和
checkSum += b;
// 取wqs偶数位,设置qlock=-1。禁用外部队列
if ((i & 1) == 0)
w.qlock = -1;
}
}
// 检测FJP是否处于稳定状态,也即没有任务出入,遍历两次
if (oldSum == (oldSum = checkSum))
break;
}
}
// 当前线程,发现队列处于稳定状态,且已经没有任何任务可执行。直接将状态变为STOP
if ((runState & STOP) == 0) {
rs = lockRunState(); // enter STOP phase
unlockRunState(rs, (rs & ~RSLOCK) | STOP);
}
}
// 此时进入STOP阶段,那么开始帮忙转变状态为terminate
int pass = 0; // 通过三个步骤来逐步帮助terminate线程池
for (long oldSum = 0L;;) { // 循环直到完成或者稳定
WorkQueue[] ws; WorkQueue w; ForkJoinWorkerThread wt; int m;
long checkSum = ctl;
// 这是不是最终状态
// 总线程数为0,也即FJP中没有活动和非活动线程,也即所有的线程都没了
if ((short)(checkSum >>> TC_SHIFT) + (config & SMASK) <= 0 ||
// 队列不存在
(ws = workQueues) == null || (m = ws.length - 1) <= 0) {
// 当前线程直接转变状态即可
if ((runState & TERMINATED) == 0) {
rs = lockRunState(); // done
// 将状态改为最终状态:TERMINATED
unlockRunState(rs, (rs & ~RSLOCK) | TERMINATED);
// 从这里立刻得知:awaitTermination,等待线程池终结是通过this对象进行阻塞
synchronized (this) { notifyAll(); } // for awaitTermination
}
break;
}
// 有线程且队列存在。那么,遍历每一个wq。只处理wq存在的队列。STOP状态含义:工作线程停止工作不管队列中是否还有任务
for (int i = 0; i <= m; ++i) {
if ((w = ws[i]) != null) {
// 计算校验和,判定队列处于稳定状态,不能放也不能取
checkSum += w.base;
// 直接禁用掉所有的队列wq。此时,不管队列里面是否有任务,都不在执行,详情请看scan方法
w.qlock = -1;
// pass为0时,为第一次进入,所以不会执行下面的语句
if (pass > 0) {
w.cancelAll(); // 将队列中剩余的任务都清空
if (pass > 1 && (wt = w.owner) != null) { // 只有内部工作队列
// 如果队列中线程还处于运行状态,那么将其中断
if (!wt.isInterrupted()) {
try { // unblock join
wt.interrupt();
} catch (Throwable ignore) {
}
}
// 如果线程处于INACTIVE状态,那么将其唤醒
if (w.scanState < 0)
U.unpark(wt); // wake up
}
}
}
}
// checksum由上面的w.base来决定,如果仍有队列不为空,这时checksum会改变,导致处于不稳定状态,那么重置pass继续循环
if (checkSum != oldSum) { // unstable
oldSum = checkSum;
pass = 0;
}
// 到达这里的判断,也即所有的队列都处于稳定状态
// 如果当前线程已经经过三次循环,还没有满足第一个判断句,也即总线程数为0,那么看看循环次数是否大于wqs的长度,如果是,那么直接退出,否则继续
else if (pass > 3 && pass > m)
break;
// 尝试将所有在等待栈中的线程全部唤醒
else if (++pass > 1) {
long c; int j = 0, sp;
while (j++ <= m && (sp = (int)(c = ctl)) != 0)
tryRelease(c, ws[sp & m], AC_UNIT);
}
}
return true;
}
- awaitTermination(…)
// 等待FJP线程状态变为TERMINATED
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
// 响应中断
if (Thread.interrupted())
throw new InterruptedException();
// common线程池是不能够被关闭的,所以直接调用awaitQuiescence,然后返回false
if (this == common) {
awaitQuiescence(timeout, unit);
return false;
}
long nanos = unit.toNanos(timeout);
// 已经终结了,那么直接返回true
if (isTerminated())
return true;
// 超时了不等待,直接返回false
if (nanos <= 0L)
return false;
// 计算等待的截止时间
long deadline = System.nanoTime() + nanos;
// 阻塞在this FJP对象的监视器锁中,直到状态变为TERMINATED然后被唤醒
synchronized (this) {
for (;;) {
if (isTerminated()) // 要么关闭
return true;
if (nanos <= 0L) // 要么超时
return false;
long millis = TimeUnit.NANOSECONDS.toMillis(nanos);
wait(millis > 0L ? millis : 1L); // 等待即可
nanos = deadline - System.nanoTime();
}
}
}
8. 加锁、等待锁、释放锁
- lockRunState()
// CAS上把锁
private int lockRunState() {
int rs;
return ((((rs = runState) & RSLOCK) != 0 ||
!U.compareAndSwapInt(this, RUNSTATE, rs, rs |= RSLOCK)) ?
awaitRunStateLock() : rs);
}
- awaitRunstateLock()
// 等待获取锁
private int awaitRunStateLock() {
Object lock;
boolean wasInterrupted = false;
for (int spins = SPINS, r = 0, rs, ns;;) {
// 锁已经被释放,那么可以去CAS竞争锁
if (((rs = runState) & RSLOCK) == 0) {
if (U.compareAndSwapInt(this, RUNSTATE, rs, ns = rs | RSLOCK)) {
if (wasInterrupted) {
try {
Thread.currentThread().interrupt();
} catch (SecurityException ignore) {
}
}
return ns;
}
}
else if (r == 0) // 初始化随机数
r = ThreadLocalRandom.nextSecondarySeed();
else if (spins > 0) { // 随机,减少自旋次数
r ^= r << 6; r ^= r >>> 21; r ^= r << 7; // 异或随机数
if (r >= 0)
--spins;
}
else if ((rs & STARTED) == 0 || (lock = stealCounter) == null)
Thread.yield(); // 由于当前rs的STARTED状态位为0,代表了,当前FJP没有在运行了,那么没有必要再去睡眠了,因为这个状态维持时间会非常短
// 光是睡眠不行,需要有人唤醒,所以这里必须置位 RSIGNAL 唤醒位,提示另外的线程需要唤醒它
else if (U.compareAndSwapInt(this, RUNSTATE, rs, rs | RSIGNAL)) {
synchronized (lock) {
if ((runState & RSIGNAL) != 0) {
try {
lock.wait();
} catch (InterruptedException ie) {
if (!(Thread.currentThread() instanceof
ForkJoinWorkerThread))
wasInterrupted = true;
}
}
else
lock.notifyAll();
}
}
}
}
- unlockRunState(…)
// 解锁
private void unlockRunState(int oldRunState, int newRunState) {
if (!U.compareAndSwapInt(this, RUNSTATE, oldRunState, newRunState)) {
Object lock = stealCounter;
runState = newRunState; // clears RSIGNAL bit
if (lock != null)
synchronized (lock) { lock.notifyAll(); }
}
}
9. TODO 🦅
- awaitJoin(…)
final int awaitJoin(WorkQueue w, ForkJoinTask<?> task, long deadline) {
int s = 0;
if (task != null && w != null) {
ForkJoinTask<?> prevJoin = w.currentJoin;
U.putOrderedObject(w, QCURRENTJOIN, task);
CountedCompleter<?> cc = (task instanceof CountedCompleter) ?
(CountedCompleter<?>)task : null;
for (;;) {
if ((s = task.status) < 0)
break;
if (cc != null)
helpComplete(w, cc, 0);
else if (w.base == w.top || w.tryRemoveAndExec(task))
helpStealer(w, task);
if ((s = task.status) < 0)
break;
long ms, ns;
if (deadline == 0L)
ms = 0L;
else if ((ns = deadline - System.nanoTime()) <= 0L)
break;
else if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) <= 0L)
ms = 1L;
if (tryCompensate(w)) {
task.internalWait(ms);
U.getAndAddLong(this, CTL, AC_UNIT);
}
}
U.putOrderedObject(w, QCURRENTJOIN, prevJoin);
}
return s;
}
- helpStealer(…)
private void helpStealer(WorkQueue w, ForkJoinTask<?> task) {
WorkQueue[] ws = workQueues;
int oldSum = 0, checkSum, m;
if (ws != null && (m = ws.length - 1) >= 0 && w != null &&
task != null) {
do { // restart point
checkSum = 0; // for stability check
ForkJoinTask<?> subtask;
WorkQueue j = w, v; // v is subtask stealer
descent: for (subtask = task; subtask.status >= 0; ) {
for (int h = j.hint | 1, k = 0, i; ; k += 2) {
if (k > m) // can't find stealer
break descent;
if ((v = ws[i = (h + k) & m]) != null) {
if (v.currentSteal == subtask) {
j.hint = i;
break;
}
checkSum += v.base;
}
}
for (;;) { // help v or descend
ForkJoinTask<?>[] a; int b;
checkSum += (b = v.base);
ForkJoinTask<?> next = v.currentJoin;
if (subtask.status < 0 || j.currentJoin != subtask ||
v.currentSteal != subtask) // stale
break descent;
if (b - v.top >= 0 || (a = v.array) == null) {
if ((subtask = next) == null)
break descent;
j = v;
break;
}
int i = (((a.length - 1) & b) << ASHIFT) + ABASE;
ForkJoinTask<?> t = ((ForkJoinTask<?>)
U.getObjectVolatile(a, i));
if (v.base == b) {
if (t == null) // stale
break descent;
if (U.compareAndSwapObject(a, i, t, null)) {
v.base = b + 1;
ForkJoinTask<?> ps = w.currentSteal;
int top = w.top;
do {
U.putOrderedObject(w, QCURRENTSTEAL, t);
t.doExec(); // clear local tasks too
} while (task.status >= 0 &&
w.top != top &&
(t = w.pop()) != null);
U.putOrderedObject(w, QCURRENTSTEAL, ps);
if (w.base != w.top)
return; // can't further help
}
}
}
}
} while (task.status >= 0 && oldSum != (oldSum = checkSum));
}
}
- helpComplete(…)
final int helpComplete(WorkQueue w, CountedCompleter<?> task,
int maxTasks) {
WorkQueue[] ws; int s = 0, m;
if ((ws = workQueues) != null && (m = ws.length - 1) >= 0 &&
task != null && w != null) {
int mode = w.config; // for popCC
int r = w.hint ^ w.top; // arbitrary seed for origin
int origin = r & m; // first queue to scan
int h = 1; // 1:ran, >1:contended, <0:hash
for (int k = origin, oldSum = 0, checkSum = 0;;) {
CountedCompleter<?> p; WorkQueue q;
if ((s = task.status) < 0)
break;
if (h == 1 && (p = w.popCC(task, mode)) != null) {
p.doExec(); // run local task
if (maxTasks != 0 && --maxTasks == 0)
break;
origin = k; // reset
oldSum = checkSum = 0;
}
else { // poll other queues
if ((q = ws[k]) == null)
h = 0;
else if ((h = q.pollAndExecCC(task)) < 0)
checkSum += h;
if (h > 0) {
if (h == 1 && maxTasks != 0 && --maxTasks == 0)
break;
r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift
origin = k = r & m; // move and restart
oldSum = checkSum = 0;
}
else if ((k = (k + 1) & m) == origin) {
if (oldSum == (oldSum = checkSum))
break;
checkSum = 0;
}
}
}
}
return s;
}
- tryCompensate(…)
private boolean tryCompensate(WorkQueue w) {
boolean canBlock;
WorkQueue[] ws; long c; int m, pc, sp;
if (w == null || w.qlock < 0 || // caller terminating
(ws = workQueues) == null || (m = ws.length - 1) <= 0 ||
(pc = config & SMASK) == 0) // parallelism disabled
canBlock = false;
else if ((sp = (int)(c = ctl)) != 0) // release idle worker
canBlock = tryRelease(c, ws[sp & m], 0L);
else {
int ac = (int)(c >> AC_SHIFT) + pc;
int tc = (short)(c >> TC_SHIFT) + pc;
int nbusy = 0; // validate saturation
for (int i = 0; i <= m; ++i) { // two passes of odd indices
WorkQueue v;
if ((v = ws[((i << 1) | 1) & m]) != null) {
if ((v.scanState & SCANNING) != 0)
break;
++nbusy;
}
}
if (nbusy != (tc << 1) || ctl != c)
canBlock = false; // unstable or stale
else if (tc >= pc && ac > 1 && w.isEmpty()) {
long nc = ((AC_MASK & (c - AC_UNIT)) |
(~AC_MASK & c)); // uncompensated
canBlock = U.compareAndSwapLong(this, CTL, c, nc);
}
else if (tc >= MAX_CAP ||
(this == common && tc >= pc + commonMaxSpares))
throw new RejectedExecutionException(
"Thread limit exceeded replacing blocked worker");
else { // similar to tryAddWorker
boolean add = false; int rs; // CAS within lock
long nc = ((AC_MASK & c) |
(TC_MASK & (c + TC_UNIT)));
if (((rs = lockRunState()) & STOP) == 0)
add = U.compareAndSwapLong(this, CTL, c, nc);
unlockRunState(rs, rs & ~RSLOCK);
canBlock = add && createWorker(); // throws on exception
}
}
return canBlock;
}
- tryRelease(…)
private boolean tryRelease(long c, WorkQueue v, long inc) {
int sp = (int)c, vs = (sp + SS_SEQ) & ~INACTIVE; Thread p;
if (v != null && v.scanState == sp) { // v is at top of stack
long nc = (UC_MASK & (c + inc)) | (SP_MASK & v.stackPred);
if (U.compareAndSwapLong(this, CTL, c, nc)) {
v.scanState = vs;
if ((p = v.parker) != null)
U.unpark(p);
return true;
}
}
return false;
}
3、ForkJoinTask
1. doExec()
// FJT的方法,由FJP的工作线程调用
volatile int status; // 当前FJT的运行状态
static final int DONE_MASK = 0xf0000000; // 取完成位的掩码
static final int NORMAL = 0xf0000000; // 正常状态
static final int CANCELLED = 0xc0000000; // 被取消了
static final int EXCEPTIONAL = 0x80000000; // 发生了异常
static final int SIGNAL = 0x00010000; // 需要唤醒
static final int SMASK = 0x0000ffff; // 取低16位的掩码
final int doExec() {
int s; boolean completed;
// 默认状态就是0,新建状态
if ((s = status) >= 0) {
try {
// 子类需要实现该方法完成调用
completed = exec();
} catch (Throwable rex) {
return setExceptionalCompletion(rex);
}
// 如果执行完成,那么设置状态为NORMAL,表示正常完成
if (completed)
s = setCompletion(NORMAL);
}
return s;
}
2. fork()
// FJT的fork方法,将任务放入FJP中执行
public final ForkJoinTask<V> fork() {
Thread t;
if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
// 如果是内部工作线程,那么直接将其放入自己的队列中 push方法
((ForkJoinWorkerThread)t).workQueue.push(this);
else
// 外部线程就调用externalPush,其实就是调用Common线程池来执行
ForkJoinPool.common.externalPush(this);
return this;
}
// FJP用于内部工作线程往自己的队列存放任务
final void push(ForkJoinTask<?> task) {
ForkJoinTask<?>[] a; ForkJoinPool p;
int b = base, s = top, n;
if ((a = array) != null) { // ignore if queue removed
int m = a.length - 1; // fenced write for task visibility
// 获取top变量的偏移量,然后放入task对象
U.putOrderedObject(a, ((m & s) << ASHIFT) + ABASE, task);
// 对top值加1
U.putOrderedInt(this, QTOP, s + 1);
// 使用putOrdered保证了STORESTORE语义
if ((n = s - b) <= 1) { // n为top引用和base引用中间的有效任务数
// 为何这里要小于等于1才唤醒等待线程,只要这里大于1了,那么必然已经都唤醒了
if ((p = pool) != null)
p.signalWork(p.workQueues, this);
}
else if (n >= m) // 满了扩容
growArray();
}
}
3. join()
// 等待当前任务执行完完成
public final V join() {
int s;
// 等待过程中发现任务执行出了异常,然后调用reportException抛出异常
if ((s = doJoin() & DONE_MASK) != NORMAL)
reportException(s);
// 正常完成就拿结果即可
return getRawResult();
}
// 真正等待的方法
private int doJoin() {
int s; Thread t; ForkJoinWorkerThread wt; ForkJoinPool.WorkQueue w;
return (s = status) < 0 ? s :
((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
(w = (wt = (ForkJoinWorkerThread)t).workQueue).
tryUnpush(this) && (s = doExec()) < 0 ? s :
wt.pool.awaitJoin(w, this, 0L) :
externalAwaitDone();
}
(p);
return true;
}
}
return false;
}
3、ForkJoinTask
1. doExec()
// FJT的方法,由FJP的工作线程调用
volatile int status; // 当前FJT的运行状态
static final int DONE_MASK = 0xf0000000; // 取完成位的掩码
static final int NORMAL = 0xf0000000; // 正常状态
static final int CANCELLED = 0xc0000000; // 被取消了
static final int EXCEPTIONAL = 0x80000000; // 发生了异常
static final int SIGNAL = 0x00010000; // 需要唤醒
static final int SMASK = 0x0000ffff; // 取低16位的掩码
final int doExec() {
int s; boolean completed;
// 默认状态就是0,新建状态
if ((s = status) >= 0) {
try {
// 子类需要实现该方法完成调用
completed = exec();
} catch (Throwable rex) {
return setExceptionalCompletion(rex);
}
// 如果执行完成,那么设置状态为NORMAL,表示正常完成
if (completed)
s = setCompletion(NORMAL);
}
return s;
}
2. fork()
// FJT的fork方法,将任务放入FJP中执行
public final ForkJoinTask<V> fork() {
Thread t;
if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
// 如果是内部工作线程,那么直接将其放入自己的队列中 push方法
((ForkJoinWorkerThread)t).workQueue.push(this);
else
// 外部线程就调用externalPush,其实就是调用Common线程池来执行
ForkJoinPool.common.externalPush(this);
return this;
}
// FJP用于内部工作线程往自己的队列存放任务
final void push(ForkJoinTask<?> task) {
ForkJoinTask<?>[] a; ForkJoinPool p;
int b = base, s = top, n;
if ((a = array) != null) { // ignore if queue removed
int m = a.length - 1; // fenced write for task visibility
// 获取top变量的偏移量,然后放入task对象
U.putOrderedObject(a, ((m & s) << ASHIFT) + ABASE, task);
// 对top值加1
U.putOrderedInt(this, QTOP, s + 1);
// 使用putOrdered保证了STORESTORE语义
if ((n = s - b) <= 1) { // n为top引用和base引用中间的有效任务数
// 为何这里要小于等于1才唤醒等待线程,只要这里大于1了,那么必然已经都唤醒了
if ((p = pool) != null)
p.signalWork(p.workQueues, this);
}
else if (n >= m) // 满了扩容
growArray();
}
}
3. join()
// 等待当前任务执行完完成
public final V join() {
int s;
// 等待过程中发现任务执行出了异常,然后调用reportException抛出异常
if ((s = doJoin() & DONE_MASK) != NORMAL)
reportException(s);
// 正常完成就拿结果即可
return getRawResult();
}
// 真正等待的方法
private int doJoin() {
int s; Thread t; ForkJoinWorkerThread wt; ForkJoinPool.WorkQueue w;
return (s = status) < 0 ? s :
((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
(w = (wt = (ForkJoinWorkerThread)t).workQueue).
tryUnpush(this) && (s = doExec()) < 0 ? s :
wt.pool.awaitJoin(w, this, 0L) :
externalAwaitDone();
}