ForkJoinPool.commonPool()实现

文章目录

前言

博主介绍:✌目前全网粉丝4W+,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领域。

涵盖技术内容:Java后端、大数据、算法、分布式微服务、中间件、前端、运维等。

博主所有博客文件目录索引:博客目录索引(持续更新)

CSDN搜索:长路

视频平台:b站-Coder长路

ForkJoinPool.commonPool() 实现

代码整体含义

这段代码的目的是:以特权权限安全地创建 ForkJoinPool 公共池

分步解析

1. java.security.AccessController.doPrivileged()

这是Java安全框架的核心方法:

  • 作用:让一段代码以"特权"方式执行,暂时提升调用代码的权限
  • 为什么需要 :因为创建线程池可能需要某些系统权限,而调用 ForkJoinPool.commonPool() 的代码可能没有这些权限

2. new java.security.PrivilegedAction<ForkJoinPool>()

这是一个接口,定义了一个需要特权执行的操作:

java 复制代码
public interface PrivilegedAction<T> {
    T run();
}

3. makeCommonPool() 方法

这是实际创建公共池的核心方法,它负责:

  • 读取系统属性(如 java.util.concurrent.ForkJoinPool.common.parallelism
  • 创建线程工厂
  • 设置异常处理器
  • 最终实例化 ForkJoinPool

为什么要这样设计?

安全原因

假设有这样的调用链:

plain 复制代码
你的业务代码
→ CompletableFuture.supplyAsync()
→ ForkJoinPool.commonPool()
→ 需要创建线程(需要系统权限)

如果没有 doPrivileged

  • JVM 会检查整个调用链上所有代码的权限
  • 如果你的业务代码没有创建线程的权限,即使 ForkJoinPool 在核心库中,也会失败

使用 doPrivileged 后:

  • makeCommonPool() 执行时,JVM 只检查 ForkJoinPool 类的权限
  • 忽略调用链上其他代码的权限检查
  • 因为核心库是可信的,所以操作可以成功

实际场景示例

java 复制代码
// 在安全限制环境下(如Applet或某些容器中)
public void myMethod() {
    // 你的代码可能没有创建线程的权限
    CompletableFuture.supplyAsync(() -> {
        // 这个任务仍然能在公共池中执行
        return expensiveCalculation();
    });
}

完整的初始化流程

ForkJoinPool 类中,公共池的初始化大致如下:

java 复制代码
public class ForkJoinPool {
    // 静态常量,保存公共池实例
    static final ForkJoinPool common;

    static {
        // 使用特权操作创建公共池
        common = java.security.AccessController.doPrivileged(
            new java.security.PrivilegedAction<ForkJoinPool>() {
                public ForkJoinPool run() {
                    return makeCommonPool();
                }
            });
    }

    private static ForkJoinPool makeCommonPool() {
        // 1. 确定并行度(CPU核心数-1,或系统属性设置)
        int parallelism = ...;

        // 2. 创建线程工厂
        ForkJoinWorkerThreadFactory factory = ...;

        // 3. 创建异常处理器
        Thread.UncaughtExceptionHandler handler = ...;

        // 4. 实例化 ForkJoinPool
        return new ForkJoinPool(parallelism, factory, handler, ...);
    }
}

makeCommonPool() 二次深入跟进

源码

核心源码:

java 复制代码
private static ForkJoinPool makeCommonPool() {
    int parallelism = -1;
    ForkJoinWorkerThreadFactory factory = null;
    UncaughtExceptionHandler handler = null;
    try {  // ignore exceptions in accessing/parsing properties
        String pp = System.getProperty
            ("java.util.concurrent.ForkJoinPool.common.parallelism");
        String fp = System.getProperty
            ("java.util.concurrent.ForkJoinPool.common.threadFactory");
        String hp = System.getProperty
            ("java.util.concurrent.ForkJoinPool.common.exceptionHandler");
        if (pp != null)
            parallelism = Integer.parseInt(pp);
        if (fp != null)
            factory = ((ForkJoinWorkerThreadFactory)ClassLoader.
                       getSystemClassLoader().loadClass(fp).newInstance());
        if (hp != null)
            handler = ((UncaughtExceptionHandler)ClassLoader.
                       getSystemClassLoader().loadClass(hp).newInstance());
    } catch (Exception ignore) {
    }
    if (factory == null) {
        if (System.getSecurityManager() == null)
            factory = new DefaultCommonPoolForkJoinWorkerThreadFactory();
        else // use security-managed default
            factory = new InnocuousForkJoinWorkerThreadFactory();
    }
    if (parallelism < 0 && // default 1 less than #cores
        (parallelism = Runtime.getRuntime().availableProcessors() - 1) <= 0)
        parallelism = 1;
    if (parallelism > MAX_CAP)
        parallelism = MAX_CAP;
    return new ForkJoinPool(parallelism, factory, handler, LIFO_QUEUE,
                            "ForkJoinPool.commonPool-worker-");
}

核心细节

核心线程数 vs 最大线程数

java 复制代码
// ForkJoinPool 的构造参数
public ForkJoinPool(int parallelism,
                    ForkJoinWorkerThreadFactory factory,
                    UncaughtExceptionHandler handler,
                    boolean asyncMode,
                    String workerThreadNamePrefix) {
    // parallelism 既是核心数也是最大数
    this(checkParallelism(parallelism),
         checkFactory(factory),
         handler,
         asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
         workerThreadNamePrefix);
}

关键特点

  • 固定线程数parallelism 参数既是核心线程数也是最大线程数
  • 无动态扩展:与传统线程池不同,ForkJoinPool 不动态创建/销毁线程
  • 工作窃取:通过工作窃取算法提高利用率,而非增加线程数

线程数量计算逻辑:

java 复制代码
// 在 common pool 中的计算
int availableProcessors = Runtime.getRuntime().availableProcessors();
int parallelism = availableProcessors - 1;  // 默认:CPU核心数-1
if (parallelism <= 0) parallelism = 1;      // 单核环境保证至少1个线程

拒绝策略

java 复制代码
// ForkJoinPool 没有显式的拒绝策略接口
// 而是通过以下机制处理任务饱和情况:

// 1. 外部提交的任务进入提交队列
public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task) {
    if (task == null) throw new NullPointerException();
    externalPush(task);  // 外部提交
    return task;
}

// 2. 内部生成的任务通过 fork() 进入工作队列
public final ForkJoinTask<V> fork() {
    Thread t;
    if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
        ((ForkJoinWorkerThread)t).workQueue.push(this);  // 内部fork
    else
        ForkJoinPool.common.externalPush(this);  // 外部线程fork
    return this;
}

实际拒绝场景:

java 复制代码
// 当线程池已关闭时提交任务
if (runState < 0) {  // 池已关闭
    throw new RejectedExecutionException();
}

// 当系统资源耗尽时
try {
    // 尝试创建新线程...
} catch (OutOfMemoryError oom) {
    // 资源耗尽,无法创建线程
    throw new RejectedExecutionException("Unable to create new thread", oom);
}

任务队列机制

工作窃取队列(Work-Stealing Queue):

java 复制代码
// 每个工作线程都有自己的双端队列
static final class WorkQueue {
    volatile int base;         // 队尾指针(窃取端)
    int top;                   // 队首指针(所有者端)
    ForkJoinTask<?>[] array;   // 任务数组
    final ForkJoinWorkerThread owner;  // 所属线程
}

LIFO vs FIFO 模式:

java 复制代码
// 在 makeCommonPool() 中使用的 LIFO_QUEUE
return new ForkJoinPool(parallelism, factory, handler, LIFO_QUEUE,
        "ForkJoinPool.commonPool-worker-");

工作窃取核心逻辑:

java 复制代码
// 工作线程的核心循环
final void runWorker(WorkQueue w) {
    w.growArray();  // 初始化队列
    for (ForkJoinTask<?> t;;) {
        if ((t = w.nextLocalTask()) != null ||  // 1. 先处理自己的任务
            (t = scan(w)) != null)              // 2. 尝试窃取其他队列的任务
            t.doExec();                         // 执行任务
        else if (tryTerminate(false, false))    // 3. 检查终止条件
            break;
    }
}

// 扫描其他队列窃取任务
private ForkJoinTask<?> scan(WorkQueue w) {
    WorkQueue[] ws; int n;
    if ((ws = workQueues) != null && (n = ws.length) > 0 && w != null) {
        for (int r = w.seed, k = r, m = n - 1;;) {
            WorkQueue q; int b;
            if ((q = ws[k & m]) != null && (b = q.base) - q.top < 0) {
                ForkJoinTask<?> t = q.pollAt(b);  // 从队尾窃取
                if (t != null)
                    return t;
            }
            // 继续扫描下一个队列...
        }
    }
    return null;
}

总结

ForkJoinPool 在处理大量可分解的并行任务时(如递归计算、流处理)具有极高的性能,但在处理大量独立短期任务时可能不如传统的 ThreadPoolExecutor 高效。

具有下面特点:

  • 固定线程池:线程数量固定,无动态扩展
  • 工作窃取:通过窃取算法实现负载均衡,而非增加线程
  • 双端队列:LIFO处理自己的任务,FIFO供其他线程窃取
  • 无显式拒绝:通过队列机制和资源限制隐式处理拒绝
  • 递归分解:专为可分治任务设计,支持任务拆分和结果合并

资料获取

大家点赞、收藏、关注、评论啦~

精彩专栏推荐订阅:在下方专栏👇🏻

更多博客与资料可查看👇🏻获取联系方式👇🏻,🍅文末获取开发资源及更多资源博客获取🍅

相关推荐
落魄江湖行7 小时前
孤舟笔记 并发篇二十九 volatile关键字有什么用?它的实现原理是什么?面试必问的轻量级同步机制
java并发·春招·孤舟笔记·volatile关键字
落魄江湖行13 小时前
孤舟笔记 并发篇二十八 wait和sleep是否会触发锁的释放及CPU资源的释放?这个区别面试必考
java并发·春招·孤舟笔记·wait和sleep
落魄江湖行1 天前
孤舟笔记 并发篇二十二 线程池是如何回收线程的?核心线程和非核心线程的回收逻辑大不相同
java并发·春招·孤舟笔记·线程池是如何回收线程的
落魄江湖行2 天前
孤舟笔记 并发篇二十五 当任务数超过核心线程数时,如何让任务不进入队列?线程池调优的经典问题
java并发·春招·孤舟笔记·当任务数超过核心线程数时
落魄江湖行2 天前
孤舟笔记 并发篇二十三 线程池是如何实现线程复用的?Worker循环取任务的秘密远比你想象的精巧
java并发·春招·孤舟笔记
落魄江湖行3 天前
孤舟笔记 并发篇十一 行锁、间隙锁、临键锁傻傻分不清?MySQL InnoDB的锁其实就这三板斧
mysql·java并发·春招·孤舟笔记
落魄江湖行4 天前
孤舟笔记 并发篇十 ReentrantLock的公平锁和非公平锁是怎么实现的?这个设计藏着大智慧
java并发·春招·孤舟笔记
阿冰冰呀7 天前
互联网大厂Java求职面试实录:谢飞机的“水货”之路
java·mybatis·dubbo·springboot·线程池·多线程·hashmap
YYYing.9 天前
【C++项目之高并发内存池 (二)】整体框架设计与ThreadCache的初步实现
笔记·高并发·线程池·c/c++
Javatutouhouduan10 天前
阿里2026最新Java面试核心讲(终极版)
java·java面试·java并发·后端开发·java程序员·java八股文·java性能优化