fork/join 框架未抛弃 aqs,而是将任务调度主干(push/pop/steal)下沉至无锁 cas 与 unsafe 内存操作,仅管理面如提交任务、线程创建等间接使用 aqs 作为辅助。

Fork/Join 框架并没有完全抛弃 AQS,而是绕开了它在任务调度层的直接依赖——它的核心调度逻辑不基于 AQS 的 CLH 队列和 park/unpark 等阻塞机制,转而用更轻量、更贴近硬件的方式实现高并发任务分发与负载均衡。
每个线程独占双端队列,天然规避全局竞争
传统 ThreadPoolExecutor 依赖一个共享的 BlockingQueue(如 LinkedBlockingQueue),所有工作线程争抢同一个队列头,必须靠 ReentrantLock 或 synchronized 加锁,本质是 AQS 支持的重入锁。而 ForkJoinPool 中:
- 每个 ForkJoinWorkerThread 绑定专属 WorkQueue(双端队列),线程只从自己队列的 top 端 pop 任务(LIFO);
- 入队(push)、出队(pop)、窃取(poll)全部通过 Unsafe 类直接操作内存偏移量,不走 Java 层锁或 AQS;
- 没有“谁先拿到队列锁”的问题,也就没有 AQS 队列中节点排队、唤醒、状态流转这些开销。
任务窃取全程使用 CAS,无锁完成跨队列协作
当线程发现本地队列为空,需从别人队列“偷”任务时,它不会去申请锁,而是:
- 随机选一个目标队列,尝试用 CAS 原子更新其 base 指针(即队尾位置),取出最老的任务;
- 这个过程不阻塞源线程——如果 CAS 失败(说明别人刚取走或正在修改),就换一个队列重试;
- 整个窃取动作不涉及任何 AQS 的 acquire/release 流程,也不触发 park/unpark,纯用户态原子指令。
仅在极少数边界场景才间接用到 AQS
ForkJoinPool 内部确实封装了少量同步逻辑,但它们不是调度主干:
- 提交外部任务(submit)时,会进入一个共享的 submission queue,该队列底层可能用到 ForkJoinPool 内置的简单锁或 CAS 控制;
- 线程创建/终止、并行度调整等管理操作,可能借助 AQS 的简单同步设施;
- 但这些都发生在“控制面”,不影响每毫秒都在执行的 task pop/push/steal 这些“数据面”高频路径。
所以准确说:Fork/Join 不是否定 AQS,而是把最热的路径——任务入队、本地执行、跨线程窃取——全部下沉到无锁原子操作层面,让 AQS 退居为辅助角色。这种设计,正是它能在多核 CPU 上跑出接近线性加速比的关键所在。











