ThreadPoolExecutor配置及CompletableFuture使用

文章详细介绍了如何在SpringBoot中配置和使用ThreadPoolExecutor,包括线程池的状态、构造方法、常用参数如核心线程数、最大线程数、阻塞队列的选择以及拒绝策略。同时,文章提到了CompletableFuture在异步编程中的作用,对比了Future的局限性,并预告了CompletableFuture的方法介绍。

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

对ThreadPoolExecutor不了解的同学可以点下面链接先了解一下,后面我们直接进行使用

百度百科ThreadPoolExecutor介绍

第一步:创建一个SpringBoot工程(快速开始教程)

第二步:自定义ThreadExecutor构造方法

package com.example.config;

import java.util.concurrent.*;

/**
 * 1、线程池状态:
 *      五种状态:
 *          线程池的状态          说明
 *          RUNNING             允许提交并处理任务
 *          SHUTDOWN            不允许提交新的任务,但是会处理完已提交的任务
 *          STOP                不允许提交新的任务,也不会处理阻塞队列中未执行的任务,并设置正在执行的线程的中断标志位
 *          TIDYING             所有任务执行完毕,池中工作的线程数为0,等待执行terminated()勾子方法
 *          TERMINATED          terminated()勾子方法执行完毕
 *
 *      线程池的shutdown() 方法,将线程池由 RUNNING(运行状态)转换为 SHUTDOWN状态
 *      线程池的shutdownNow()方法,将线程池由RUNNING 或 SHUTDOWN 状态转换为 STOP 状态。
 *      注:SHUTDOWN 状态 和 STOP 状态 先会转变为 TIDYING 状态,最终都会变为 TERMINATED
 *
 * 2、ThreadPoolExecutor构造函数:
 *      ThreadPoolExecutor继承自AbstractExecutorService,而AbstractExecutorService实现了ExecutorService接口。
 *
 *      当调用线程池execute() 方法添加一个任务时,线程池会做如下判断:
 *          如果有空闲线程,则直接执行该任务;
 *          如果没有空闲线程,且当前运行的线程数少于corePoolSize,则创建新的线程执行该任务;
 *          如果没有空闲线程,且当前的线程数等于corePoolSize,同时阻塞队列未满,则将任务入队列,而不添加新的线程;
 *          如果没有空闲线程,且阻塞队列已满,同时池中的线程数小于maximumPoolSize ,则创建新的线程执行任务;
 *          如果没有空闲线程,且阻塞队列已满,同时池中的线程数等于maximumPoolSize ,则根据构造函数中的 handler 指定的策略来拒绝新的任务。
 *
 *      当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。
 *      注:如果线程池设置了allowCoreThreadTimeout参数为true(默认false),那么当空闲线程超过keepaliveTime后直接停掉。(不会判断线程数是否大于corePoolSize)即:最终线程数会变为0。
 *
 *      ThreadPoolExecutor线程池推荐了三种等待队列,它们是:SynchronousQueue 、LinkedBlockingQueue 和 ArrayBlockingQueue。
 *          1)有界队列:
 *              SynchronousQueue :一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于 阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法 Executors.newCachedThreadPool 使用了这个队列。
 *              ArrayBlockingQueue:一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。
 *          2)无界队列:
 *              LinkedBlockingQueue:基于链表结构的无界阻塞队列,它可以指定容量也可以不指定容量(实际上任何无限容量的队列/栈都是有容量的,这个容量就是Integer.MAX_VALUE)
 *              PriorityBlockingQueue:是一个按照优先级进行内部元素排序的无界阻塞队列。队列中的元素必须实现 Comparable 接口,这样才能通过实现compareTo()方法进行排序。优先级最高的元素将始终排在队列的头部;PriorityBlockingQueue 不会保证优先级一样的元素的排序。
 *          注意:keepAliveTime和maximumPoolSize及BlockingQueue的类型均有关系。如果BlockingQueue是无界的,那么永远不会触发maximumPoolSize,自然keepAliveTime也就没有了意义。
 *
 *      如果不指定线程工厂时,ThreadPoolExecutor 会使用ThreadPoolExecutor.defaultThreadFactory 创建线程。默认工厂创建的线程:同属于相同的线程组,具有同为 Thread.NORM_PRIORITY 的优先级,以及名为 “pool-XXX-thread-” 的线程名(XXX为创建线程时顺序序号),且创建的线程都是非守护进程。
 *
 *      handler :表示当 workQueue 已满,且池中的线程数达到 maximumPoolSize 时,线程池拒绝添加新任务时采取的策略。(可以不指定)
 *                          策略                                                            说明
 *              ThreadPoolExecutor.AbortPolicy()                        抛出RejectedExecutionException异常。默认策略
 *              ThreadPoolExecutor.CallerRunsPolicy()                   由向线程池提交任务的线程来执行该任务
 *              ThreadPoolExecutor.DiscardPolicy()                      抛弃当前的任务
 *              ThreadPoolExecutor.DiscardOldestPolicy()                抛弃最旧的任务(最先提交而没有得到执行的任务)
 *
 *  3、常用方法:
 *      除了在创建线程池时指定上述参数的值外,还可在线程池创建以后通过如下方法进行设置。
 *          setCorePoolSize(int corePoolSize)                                       线程池中核心线程数的最大值
 *          setMaximumPoolSize(int maximumPoolSize)                                 线程池中能拥有最多线程数
 *          setKeepAliveTime(long time, TimeUnit unit)                              表示空闲线程的存活时间,表示keepAliveTime的单位
 *          allowCoreThreadTimeOut(boolean value)                                   设置策略,该策略控制在保持活动时间内没有任务到达时核心线程是否可以超时和终止,并在新任务到达时根据需要替换。如果为 false,则核心线程永远不会因缺少传入任务而终止。如果为 true,则应用于非核心线程的相同保持活动状态策略也适用于核心线程。为避免连续替换线程,设置 {@code true} 时保持活动时间必须大于零。通常,应在主动使用池之前调用此方法。@param值 {@code true} 如果应该超时,否则 {@code false} @throws IllegalArgumentException 如果值为 {@code true} 并且当前保持活动时间不大于零
 *          setThreadFactory(ThreadFactory threadFactory)                           用于缓存任务的阻塞队列,它决定了缓存任务的排队策略
 *          setRejectedExecutionHandler(RejectedExecutionHandler handler)           指定创建线程的工厂。(可以不指定)
 *
 *      此外,还有一些方法:
 *          getCorePoolSize():返回线程池的核心线程数,这个值是一直不变的,返回在构造函数中设置的coreSize大小;
 *          getMaximumPoolSize():返回线程池的最大线程数,这个值是一直不变的,返回在构造函数中设置的coreSize大小;
 *          getLargestPoolSize():记录了曾经出现的最大线程个数(水位线);
 *          getPoolSize():线程池中当前线程的数量;
 *          getActiveCount():Returns the approximate(近似) number of threads that are actively executing tasks;
 *          prestartAllCoreThreads():会启动所有核心线程,无论是否有待执行的任务,线程池都会创建新的线程,直到池中线程数量达到 corePoolSize;
 *          prestartCoreThread():会启动一个核心线程(同上)。
 *
 * 4、Executors类:
 *      Executors类的底层实现便是ThreadPoolExecutor! Executors 工厂方法有:
 *          Executors.newCachedThreadPool():无界线程池,可以进行自动线程回收
 *          Executors.newFixedThreadPool(int):固定大小线程池
 *          Executors.newSingleThreadExecutor():单个后台线程
 *          它们均为大多数使用场景预定义了设置。不过在阿里java文档中说明,尽量不要用该类创建线程池。
 *
 */
public class TestThreadExecutor extends ThreadPoolExecutor {

    /**
     * 创建线程执行器
     *
     * @param corePoolSize    线程池中核心线程数的最大值
     * @param maximumPoolSize 线程池中能拥有最多线程数
     * @param keepAliveTime   表示空闲线程的存活时间
     * @param unit            表示keepAliveTime的单位
     * @param workQueue       用于缓存任务的阻塞队列,它决定了缓存任务的排队策略
     * @param threadFactory   指定创建线程的工厂。(可以不指定)
     * @param handler         拒绝策略
     */
    public TestThreadExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        super(corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler);
    }
}

第三步:创建ThreadPoolExecutor,传入参数配置

package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@EnableAsync//开启异步支持
@Configuration
public class ThreadExecutorConfig {

    @Bean(name = "testThreadExecutor")
    public ThreadPoolExecutor threadPoolExecutor() {
        return new TestThreadExecutor(
                50,
                1000,
                60,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(10000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
    }
}

第四步:创建测试类测试

package com.example;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
@SpringBootTest
public class ThreadExecutorTest {

    @Autowired
    private ThreadPoolExecutor executor;

    @Test
    public void test01() {
        CompletableFuture.runAsync(() -> {
            log.info("进入异步任务执行");
            log.info(Thread.currentThread().getName());
            log.info("异步任务执行结束");
        }, executor);
    }
}

---------------------------------------------------------------------------------------------------------------------------------

CompletableFuture介绍:

        CompletableFuture 用于 Java 中的异步编程。异步编程是一种编写非阻塞代码的方法,方法是在与主应用程序线程不同的线程上运行任务,并通知主线程其进度、完成或失败。
        这样,您的主线程不会阻塞/等待任务的完成,它可以并行执行其他任务。拥有这种并行性极大地提高了程序的性能。

Future vs CompletableFuture

        CompletableFuture 是Java 8 中引入的 Java Future API的扩展。

        Future 用作对异步计算结果的引用。它提供了isDone()一种检查计算是否完成的get()方法,以及一种在计算完成时检索计算结果的方法。

        Future API 是向 Java 异步编程迈出的一大步,但它缺乏一些重要且有用的特性。

Future局限性

  • 无法手动完成:

    假设您编写了一个接口来从远程 API 获取商品信息。由于此 API 调用非常耗时,因此您在单独的线程中运行它并从您的函数中返回一个 Future。

    现在,让我们假设,如果远程API服务关闭了,那么您希望根据商品的最后一次缓存价格手动完成Future。你能在Future上做到这一点吗?不!

  • 你不能在没有阻塞的情况下对 Future 的结果执行进一步的操作:

    Future 不会通知你它的完成。它提供了一个阻塞get()方法,直到结果可用。

    您无法将回调函数附加到 Future 并在 Future 的结果可用时自动调用它。

  • 多个Future不能链接在一起:

    有时您需要执行长时间运行的计算,当计算完成后,您需要将其结果发送到另一个长时间运行的计算,依此类推。

  • 您不能将多个Future组合在一起:

    假设您有 10 个不同的 Futures 想要并行运行,然后在它们全部完成后运行某个函数。你不能用 Future 做到这一点。

  • 无异常处理:

    Future API 没有任何异常处理结构。

CompletableFuture方法介绍:

未完待续......

        

<think>嗯,用户想了解CompletableFuture的原理和详细实现,特别是Java异步编程方面的深入分析。首先,我需要回顾一下之前提供的引用内容,看看有哪些信息可以利用。引用[1]提到CompletableFutureJava 8引入的,用于异步编程,属于java.util.concurrent包,相比传统Future更灵活,支持链式调用、任务合并和异常处理。引用[2]强调了它的高效异步模式和并发编程的简洁性。引用[3]指出CompletableFuture有很多方法和内部类,需要分步分析。引用[4]提到了Java并发的发展,包括ForkJoin框架等。 用户的问题是关于原理和实现的详细解析,所以需要深入讨论内部机制,比如如何实现异步任务的管理、回调链的处理、线程池的使用,以及如何组合多个任务。可能还需要涉及CompletableFuture的状态机、依赖关系处理、异常传播机制等。 首先,CompletableFuture基于ForkJoinPool,这点应该在实现原理部分提到。然后,它的核心结构可能涉及到每个阶段(stage)作为一个节点,形成链表或栈结构来处理依赖。状态管理方面,可能用volatile变量来保存结果和状态变化,确保可见性。 另外,需要解释CompletableFuture如何通过thenApply、thenAccept等方法链式组合任务,这些方法如何注册回调,并在前一个阶段完成时触发后续操作。这里可能涉及CompletionStage接口的实现,以及各种不同类型的Completion子类,比如UniApply、BiApply等,处理不同的依赖情况。 异常处理也是一个重点,CompletableFuture如何捕获异常并传递给后续的exceptionally或handle方法。需要说明当某个阶段出现异常时,后续依赖的阶段如何被触发,并处理异常结果。 线程池的集成部分,用户可能想知道默认使用的线程池,以及如何自定义。比如,默认使用ForkJoinPool.commonPool(),但也可以指定自定义的Executor。在实现中,如何将任务提交到线程池,并在任务完成后执行回调。 还有,组合多个CompletableFuture的方法,如allOf、anyOf,这些是如何实现的,可能涉及内部计数器或条件判断,等待所有或任意一个Future完成。 另外,性能优化方面,CompletableFuture如何减少线程阻塞,通过非阻塞的回调机制提高效率。对比传统的Future.get(),CompletableFuture通过回调通知机制避免了阻塞等待。 可能用户还想了解与响应式编程的区别,或者与其他异步框架如RxJava的不同之处,但根据问题,重点应放在CompletableFuture本身的原理和实现细节上。 需要确保回答结构清晰,分点讲解核心原理、状态管理、依赖处理、线程池机制、组合任务和异常处理等部分。同时,结合代码示例说明,比如创建异步任务的方法,链式调用的例子,以及异常处理的示例。 最后,生成相关问题时要围绕用户可能的进一步疑问,比如与传统的Future对比、如何选择线程池、实际应用场景、性能优化方法、异常处理最佳实践,以及和响应式编程的结合等。</think># CompletableFuture原理与实现深度解析 ## 一、核心实现原理 CompletableFuture基于观察者模式构建异步任务链,内部采用$stage$链表结构管理任务依赖关系。每个阶段通过$UniCompletion$/$BiCompletion$等内部类实现回调注册,采用无锁CAS操作保证线程安全[^3]。 状态机实现包含三个关键变量: $$ \begin{cases} result & \text{存储计算结果/异常对象} \\ stack & \text{等待触发的回调函数栈} \\ nest & \text{防止递归调用计数器} \end{cases} $$ ## 二、任务执行流程 1. **异步启动**:通过`supplyAsync()`创建任务时,默认使用$ForkJoinPool.commonPool()$[^4] ```java CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> { // 异步任务逻辑 return "result"; }); ``` 2. **依赖传播**:每个`thenApply()`调用生成新的$UniApply$节点并压入前驱的栈顶 3. **完成触发**:当前任务完成后,通过`postComplete()`方法反向遍历栈节点触发后续操作 ## 三、线程池集成机制 | 配置方式 | 实现类 | 适用场景 | |-------------------|-------------------------|-----------------------| | 默认线程池 | ForkJoinPool.commonPool | CPU密集型任务 | | 自定义线程池 | ThreadPoolExecutor | IO密集型任务 | | 直接执行 | Runnable::run | 快速回调(同线程执行) | ## 四、组合任务实现 1. **AND聚合**:`allOf()`通过$BiRelay$合并多个future结果 2. **OR聚合**:`anyOf()`使用$AtomicReference$竞争设置首个完成结果 3. **嵌套组合**:`thenCompose()`通过$UniCompose$实现future级联 ## 五、异常处理机制 1. 异常传播路径:通过$AltResult$包装异常对象沿任务链传递 2. 恢复机制:`exceptionally()`注册的恢复函数可返回替代值 3. 全局捕获:`handle()`方法同时接收结果和异常参数 ```java CompletableFuture.supplyAsync(() -> { if (new Random().nextBoolean()) throw new RuntimeException("模拟异常"); return "data"; }).exceptionally(ex -> { System.out.println("捕获异常: " + ex.getMessage()); return "default"; }); ``` ## 六、性能优化设计 1. 延迟提交:使用$LazyExecutor$减少线程切换开销 2. 栈优化:采用Treiber栈结构提升并发压栈性能[^2] 3. 对象复用:内部$ASYNC$/$NIL$等标记对象减少内存分配
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值