关于Java中ForkJoinPool.commonPool()详解

2 篇文章 0 订阅
本文分析了Java8中并行流(parallelStream)和CompletableFuture的底层实现,特别关注它们是否使用线程池,以及如何配置ForkJoinPool.commonPool。作者揭示了ForkJoinPool在处理并行任务时的角色和默认设置。
摘要由CSDN通过智能技术生成

先抛两个问题出来:

问题一:

在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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值