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供其他线程窃取
  • 无显式拒绝:通过队列机制和资源限制隐式处理拒绝
  • 递归分解:专为可分治任务设计,支持任务拆分和结果合并

资料获取

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

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

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

相关推荐
努力发光的程序员20 小时前
互联网大厂Java求职面试实录
java·jvm·线程池·多线程·hashmap·juc·arraylist
利刃大大1 天前
【JavaSE】十五、线程同步wait | notify && 单例模式 && 阻塞队列 && 线程池 && 定时器
java·单例模式·线程池·定时器·阻塞队列
JohnnyDeng941 天前
ArkTs-线程池工具封装
线程池·arkts·鸿蒙
妮妮喔妮6 天前
线程池监控的五大指标
线程池
自在极意功。13 天前
Socket学习
tomcat·socket·线程池
Super小白&15 天前
C++ 高可用线程池实现:核心 / 非核心线程动态扩缩容 + 任务超时监控
c++·线程池
Unlyrical17 天前
线程池详解(c++手撕线程池)
c++·线程·线程池·c++11
better_liang19 天前
每日Java面试场景题知识点之-线程池配置与优化
java·性能优化·面试题·线程池·并发编程
luoyayun36121 天前
Qt/C++ 线程池TaskPool与 Worker 框架实践
c++·qt·线程池·taskpool