关于Java中ForkJoinPool.commonPool()详解

本文分析了Java8中并行流(parallelStream)和CompletableFuture的底层实现,特别关注它们是否使用线程池,以及如何配置ForkJoinPool.commonPool。作者揭示了ForkJoinPool在处理并行任务时的角色和默认设置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先抛两个问题出来:

问题一:

在jdk8中引入了一个重要的api,那就是流(stream)相关的处理,这使得我们在处理集合等数据时和之前相比变得方便了许多,并且,它还支持并行流处理(paralleStream)。那么,问题来了:在并行流(parallelStream)处理当中,它有没有用到线程池?怎么配置的?如果去并行处理一个很大的集合,它会不会使得cpu长时间处于一个高负载的情况进而影响服务整体性能?

问题二:

在jdk8中还引入一个CompletableFuture,它也是一个很重要的概念,我们可以使用它来实现一些异步编程的逻辑。那么,我的问题是,在它提供的静态方法如supplyAsync(Supplier<U> supplier)和runAsync(Runnable runnable)这些方法构建的异步任务中,有没有使用到线程池?

 

带着这两个问题,我们先进入jdk8的源码中找找答案

CompletableFuture:

parallelStream:

StreamSupport.stream:

最后,在AbstractPipeline中我们找到:

而这个evaluateParallel中,实际会根据terminalOp的不同,调用不同的实现,而这些terminalOpTask(如:ForEachOp)继承自CountedCompleter,而CountedCompleter又继承看ForkJoinTask,它,也使用到了ForkJoinPool.commonPool()

        这里虽然没有深入源码,但是可以简单总结一下:(为什么要提这部分?因为面试总少不了有人会拿这两个例子来问,先引入,后展开讲讲commonPool,所以:))

        在JDK8中,有多个API的默认实现都使用了ForkJoinPool.commonPool()提供的线程池。这些 API 主要涉及并行流(parallel streams)、CompletableFuture等(Stream也是ForkJoin框架的典型应用)。

好了,终于开始切入主题了,ForkJoinPool.commonPool(),一起来看看!

    /**
     * Common (static) pool. Non-null for public use unless a static
     * construction exception, but internal usages null-check on use
     * to paranoically avoid potential initialization circularities
     * as well as to simplify generated code.
     */
    static final ForkJoinPool common;
...
    /**
     * Returns the common pool instance. This pool is statically
     * constructed; its run state is unaffected by attempts to {@link
     * #shutdown} or {@link #shutdownNow}. However this pool and any
     * ongoing processing are automatically terminated upon program
     * {@link System#exit}.  Any program that relies on asynchronous
     * task processing to complete before program termination should
     * invoke {@code commonPool().}{@link #awaitQuiescence awaitQuiescence},
     * before exit.
     *
     * @return the common pool instance
     * @since 1.8
     */
    public static ForkJoinPool commonPool() {
        // assert common != null : "static init error";
        return common;
    }


从它的方法定义中可以看到返回的是一个公共的池实例,也就是ForkJoinPool实例。它的state不受尝试shutdown或shutdownNow方法的影响。它的状态、以及正在运行的处理都会在System.exit时自动终止。

我们再来看看common是怎么实例化的

static {
    ...
    //实例化,static代码块中实现
    common = java.security.AccessController.doPrivileged
            (new java.security.PrivilegedAction<ForkJoinPool>() {
                public ForkJoinPool run() { return makeCommonPool(); }});
    ...
}

继续,makeCommonPool方法的实现:

    /**
     * Creates and returns the common pool, respecting user settings
     * specified via system properties.
     */
    private static ForkJoinPool makeCommonPool() {

        final ForkJoinWorkerThreadFactory commonPoolForkJoinWorkerThreadFactory =
                new CommonPoolForkJoinWorkerThreadFactory();
        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 = commonPoolForkJoinWorkerThreadFactory;
            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-");
    }

        从makeCommonPool的实现中,我们可以看到,它读取了三个系统属性pp、fp、hp,分别表示并行数、线程工厂、异常处理器,也就是说,我们是可以通过System.setProperty设置对应的属性来干预这个公共线程池的构造参数的

        parallelism:并行参数默认值是可用处理器数-1,如果处理器数只有1,则默认并行为1;

        factory:如果未指定,且SecurityManager(安全管理器)为null,则使用默认的commonPoolForkJoinWorkerThreadFactory,否则,使用无害工作线程工厂InnocuousForkJoinWorkerThreadFactory;

        handler:如果未指定,则默认为null,即:针对异常不做任何处理。

好了,到这里,基本都已经了解了,ForkJoinPool.commonPool()是什么,用在了哪些场景,怎么干预它的参数。

最后,再看下javadoc中关于ForkJoinPool的说明吧,其中也介绍了关于commonPool()的说明,巩固一下,顺便学学英语。

Class ForkJoinPool
java.lang.Object
java.util.concurrent.AbstractExecutorService
java.util.concurrent.ForkJoinPool
All Implemented Interfaces:
Executor, ExecutorService

public class ForkJoinPool
extends AbstractExecutorService
An ExecutorService for running ForkJoinTasks. A ForkJoinPool provides the entry point for submissions from non-ForkJoinTask clients, as well as management and monitoring operations.
A ForkJoinPool differs from other kinds of ExecutorService mainly by virtue of employing work-stealing: all threads in the pool attempt to find and execute tasks submitted to the pool and/or created by other active tasks (eventually blocking waiting for work if none exist). This enables efficient processing when most tasks spawn other subtasks (as do most ForkJoinTasks), as well as when many small tasks are submitted to the pool from external clients. Especially when setting asyncMode to true in constructors, ForkJoinPools may also be appropriate for use with event-style tasks that are never joined.

A static commonPool() is available and appropriate for most applications. The common pool is used by any ForkJoinTask that is not explicitly submitted to a specified pool. Using the common pool normally reduces resource usage (its threads are slowly reclaimed during periods of non-use, and reinstated upon subsequent use).

For applications that require separate or custom pools, a ForkJoinPool may be constructed with a given target parallelism level; by default, equal to the number of available processors. The pool attempts to maintain enough active (or available) threads by dynamically adding, suspending, or resuming internal worker threads, even if some tasks are stalled waiting to join others. However, no such adjustments are guaranteed in the face of blocked I/O or other unmanaged synchronization. The nested ForkJoinPool.ManagedBlocker interface enables extension of the kinds of synchronization accommodated.

In addition to execution and lifecycle control methods, this class provides status check methods (for example getStealCount()) that are intended to aid in developing, tuning, and monitoring fork/join applications. Also, method toString() returns indications of pool state in a convenient form for informal monitoring.

As is the case with other ExecutorServices, there are three main task execution methods summarized in the following table. These are designed to be used primarily by clients not already engaged in fork/join computations in the current pool. The main forms of these methods accept instances of ForkJoinTask, but overloaded forms also allow mixed execution of plain Runnable- or Callable- based activities as well. However, tasks that are already executing in a pool should normally instead use the within-computation forms listed in the table unless using async event-style tasks that are not usually joined, in which case there is little difference among choice of methods.

Summary of task execution methods
Call from non-fork/join clients	Call from within fork/join computations
Arrange async execution	execute(ForkJoinTask)	ForkJoinTask.fork()
Await and obtain result	invoke(ForkJoinTask)	ForkJoinTask.invoke()
Arrange exec and obtain Future	submit(ForkJoinTask)	ForkJoinTask.fork() (ForkJoinTasks are Futures)
The common pool is by default constructed with default parameters, but these may be controlled by setting three system properties:

java.util.concurrent.ForkJoinPool.common.parallelism - the parallelism level, a non-negative integer
java.util.concurrent.ForkJoinPool.common.threadFactory - the class name of a ForkJoinPool.ForkJoinWorkerThreadFactory
java.util.concurrent.ForkJoinPool.common.exceptionHandler - the class name of a Thread.UncaughtExceptionHandler
If a SecurityManager is present and no factory is specified, then the default pool uses a factory supplying threads that have no Permissions enabled. The system class loader is used to load these classes. Upon any error in establishing these settings, default parameters are used. It is possible to disable or limit the use of threads in the common pool by setting the parallelism property to zero, and/or using a factory that may return null. However doing so may cause unjoined tasks to never be executed.
Implementation notes: This implementation restricts the maximum number of running threads to 32767. Attempts to create pools with greater than the maximum number result in IllegalArgumentException.

This implementation rejects submitted tasks (that is, by throwing RejectedExecutionException) only when the pool is shut down or internal resources have been exhausted.

Since:
1.7

<think>嗯,用户让我介绍一下ForkJoinPool和CompletableFuture。首先,我需要确认自己对这两个概念的理解是否正确,然后再组织语言解释清楚。 ForkJoinPoolJava中的一个线程池,对吧?我记得它是用来处理分治任务的,比如把大任务拆分成小任务,并行处理,最后合并结果。应该和RecursiveTask或者RecursiveAction有关。它的工作窃取算法也很重要,能提高效率,防止线程闲置。这部分需要详细说明一下原理和适用场景。 然后是CompletableFuture,这是Java 8引入的,用于异步编程。它支持链式调用,可以处理复杂的异步操作组合,比如thenApply、thenCompose这些方法。相比以前的Future,CompletableFuture更灵活,可以手动完成,处理异常,或者组合多个Future。可能需要举例说明如何用它来编排多个任务,比如流水线处理或者合并结果。 接下来要考虑用户可能的背景。可能是Java开发者,想要优化并发性能,或者处理复杂的异步任务。用户可能在使用传统线程池或Future时遇到了限制,想了解更高级的工具。所以需要比较ForkJoinPool和普通线程池的区别,以及CompletableFuture相比Future的优势。 还要注意用户的问题中提到的系统级指令,比如数学公式的格式,但这里应该不需要数学公式。不过要确保回答结构清晰,分点说明,可能用代码示例辅助解释。但用户没有要求代码,所以可能只需文字说明。 需要检查两者的关联点。比如,CompletableFuture默认使用ForkJoinPool吗?是的,Java 8中CompletableFuture的异步任务默认使用ForkJoinPool.commonPool(),不过也可以指定其他线程池。这点可能需要提到,说明它们如何配合使用。 是否有常见的错误或使用注意事项?比如ForkJoinPool适合计算密集型任务,而IO密集型可能不合适;CompletableFuture的异常处理需要特别注意,比如使用handle或exceptionally方法。 总结部分需要强调两者的用途不同,ForkJoinPool用于任务分解执行,CompletableFuture用于异步编程和任务组合,但可以结合使用提升性能。这样用户能明确各自的适用场景和优势。</think>### ForkJoinPool 与 CompletableFuture 详解 #### 一、ForkJoinPool(分而治之的线程池) 1. **核心思想** ForkJoinPoolJava 7 引入的线程池,基于 **分治算法(Divide and Conquer)** 设计,适用于 **递归分解的并行任务**。其核心机制为: - **任务拆分**:将大任务递归拆分为子任务(`Fork`)。 - **工作窃取(Work-Stealing)**:空闲线程主动从其他线程的任务队列“窃取”任务,提升 CPU 利用率。 2. **关键组件** - `ForkJoinTask`:抽象基类,具体实现为: - `RecursiveTask`:有返回值的任务(如计算斐波那契数列)。 - `RecursiveAction`:无返回值的任务(如遍历文件系统)。 3. **适用场景** - 计算密集型任务(如排序、矩阵运算)。 - 递归型任务(如树遍历、归并排序)。 - **不适用** I/O 密集型任务(会阻塞线程池)。 4. **示例代码逻辑** ```java class SumTask extends RecursiveTask<Long> { // 拆分逻辑:将数组分成两半,分别计算后合并结果 @Override protected Long compute() { if (任务足够小) return 直接计算; else { SumTask left = new SumTask(前半部分); SumTask right = new SumTask(后半部分); left.fork(); // 异步执行子任务 return right.compute() + left.join(); // 合并结果 } } } ``` --- #### 二、CompletableFuture(异步编程利器) 1. **核心思想** Java 8 引入的异步编程工具,用于 **编排多任务流水线**,解决传统 `Future` 的阻塞问题。特点包括: - **链式调用**:通过 `thenApply`, `thenCompose` 等方法串联任务。 - **组合操作**:支持 `allOf`, `anyOf` 合并多个 Future。 - **异常处理**:通过 `exceptionally`, `handle` 捕获异常。 2. **关键能力** - **异步执行**:默认使用 `ForkJoinPool.commonPool()`,可指定自定义线程池。 - **结果传递**:将前序任务结果传递给后续任务。 - **依赖控制**:定义任务完成后的回调逻辑(成功/失败)。 3. **典型场景** - 多个异步任务按顺序或并行执行(如调用多个微服务后合并结果)。 - 超时控制、降级处理(如 `orTimeout`, `completeOnTimeout`)。 - 响应式编程(与 Reactor/RxJava 类似但更轻量)。 4. **示例代码逻辑** ```java CompletableFuture.supplyAsync(() -> 查询用户信息) // 异步任务1 .thenApply(user -> 查询用户订单(user)) // 依赖任务1的结果 .thenAccept(orders -> 发送通知(orders)) // 消费最终结果 .exceptionally(ex -> { 记录异常; return null; }); // 异常处理 ``` --- #### 三、对比与协作 | **维度** | ForkJoinPool | CompletableFuture | |------------------|-------------------------------|------------------------------| | **目标** | 任务分解与并行计算 | 异步任务编排与结果处理 | | **线程模型** | 工作窃取线程池 | 基于线程池(默认 ForkJoinPool)| | **适用层级** | 底层任务执行 | 高层业务逻辑编排 | | **协作方式** | 可被 CompletableFuture 调用 | 可指定使用 ForkJoinPool | --- #### 四、最佳实践 1. **ForkJoinPool** - 设置合理的 **阈值**(避免过度拆分任务)。 - 避免阻塞操作(如 I/O)占用工作线程。 2. **CompletableFuture** - 使用 `自定义线程池` 隔离资源(如 I/O 任务与计算任务分开)。 - 优先使用 `thenCompose`(扁平化嵌套 Future)而非 `thenApply`。 --- #### 五、总结 - **ForkJoinPool** 是并行计算的底层引擎,适合 **递归分解型任务**。 - **CompletableFuture** 是异步编程的上层工具,擅长 **编排复杂任务链**。 - 二者可结合使用:例如用 `CompletableFuture` 调度任务,底层由 `ForkJoinPool` 执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jack_abu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值