多线程之CompletableFuture的用法

CompletableFuture的用法如下
executor 对象是我自定义的一个线程池,其具体代码如下,我通过@Autowired和@Qualifier注解通过name别名注入的方式注入

@Configuration
public class AsyncConfiguration {

    @Autowired
    private ThreadExecutorPoolLogProperties logProperties;

    @Autowired
    private ThreadExecutorPoolTaskProperties taskProperties;


    @Bean("taskExecutor")
    public Executor taskExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(taskProperties.getCorePoolSize());
        // 设置最大线程数
        executor.setMaxPoolSize(taskProperties.getMaxPoolSize());
        // 设置队列容量
        executor.setQueueCapacity(taskProperties.getQueueCapacity());
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(taskProperties.getKeepAliveSeconds());
        //等待时间 (默认为0,此时立即停止),并没等待xx秒后强制停止
        executor.setAwaitTerminationSeconds(taskProperties.getAwaitTerminationSeconds());
        // 设置默认线程名称
        executor.setThreadNamePrefix("task-");
        /** 设置拒绝策略 为 调用者运行策略(即 使用当前线程同同步处理)
         * CallerRunsPolicy(调用者运行策略)
         * AbortPolicy(中止策略)
         * DiscardPolicy(丢弃策略)
         * DiscardOldestPolicy(弃老策略)
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }
}

1、异步执行任务(无返回值)

    @Autowired
    @Qualifier("taskExecutor")
    private Executor executor;

    //异步执行任务(无返回值)
    @GetMapping("/test1")
    public void testMethod(){
        log.info("我是主线程1");
        CompletableFuture.runAsync(()->{
            try {
                Thread.sleep(5000l);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("我是异步执行");
        },executor);
        log.info("我是主线程2");
    }

控制台打印结果

[:10.79.8.110:7001] [4d517ac72d623da8,] 2025-10-14 10:45:10.850 INFO 68788 [http-nio-7001-exec-6] com.xxxx.TestController 我是主线程1
[:10.79.8.110:7001] [4d517ac72d623da8,] 2025-10-14 10:45:10.851 INFO 68788 [http-nio-7001-exec-6] com.xxxx.TestController 我是主线程2
[:10.79.8.110:7001] [4d517ac72d623da8,] 2025-10-14 10:45:15.854 INFO 68788 [task-3] com.xxxx.TestController 我是异步执行

2、异步执行任务(有返回值)
具体代码如下:

    @GetMapping("/test2")
    public void testMethod2() throws ExecutionException, InterruptedException {
        log.info("我是主线程1");
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(5000l);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("我是异步执行");
            return "我是异步返回值结果";
        }, executor);
        log.info("我是主线程2");
        //future.get()有阻塞作用
        String result = future.get();
        log.info("异步返回值结果:{}",result);
    }

控制台打印结果如下:

[:10.79.8.110:7001] [156778a6694bf28d,] 2025-10-14 10:49:54.556 INFO 68788 [http-nio-7001-exec-9] com.xxxx.TestController 我是主线程1
[:10.79.8.110:7001] [156778a6694bf28d,] 2025-10-14 10:49:54.559 INFO 68788 [http-nio-7001-exec-9] com.xxxx.TestController 我是主线程2
[:10.79.8.110:7001] [156778a6694bf28d,] 2025-10-14 10:49:59.559 INFO 68788 [task-4] com.xxxx.TestController 我是异步执行
[:10.79.8.110:7001] [156778a6694bf28d,] 2025-10-14 10:49:59.560 INFO 68788 [http-nio-7001-exec-9] com.xxxx.TestController 异步返回值结果:我是异步返回值结果

从控制台可以看出,future.get()方法有阻塞作用。

3、链式调用:thenApply() 处理结果

    //链式调用:thenApply() 处理结果
    @GetMapping("/test3")
    public void testMethod3() throws ExecutionException, InterruptedException {
        log.info("我是主线程1");
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            return "Hello";
        }, executor)
        .thenApply(s -> s+"World") //s是上一个任务的结果
        .thenApply(s-> s+"China");//继续处理
        String result = future.get();
        log.info("异步返回值结果:{}",result);
    }

控制台结果:

[:10.79.8.110:7001] [aacf023b4640ada6,] 2025-10-14 10:57:06.321 INFO 64564 [http-nio-7001-exec-2] com.xxxx.TestController 我是主线程1
[:10.79.8.110:7001] [aacf023b4640ada6,] 2025-10-14 10:57:06.327 INFO 64564 [http-nio-7001-exec-2] com.xxxx.TestController 异步返回值结果:HelloWorldChina

4、消费结果:thenAccept() 不返回新结果。简单说,thenAccept() 就像一个 “终结者” 操作 —— 它负责处理结果,但不会把处理后的内容传递给下一个步骤,适合作为异步流程的收尾操作(如日志记录、结果通知等)。

    //消费结果:thenAccept() 不返回新结果
    @GetMapping("/test4")
    public void testMethod4() throws ExecutionException, InterruptedException {
        log.info("我是主线程1");
        CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
                    return "计算结果: " + (100 + 200);
                }, executor)
                .thenAccept(s -> {
                    log.info("最终数据结果为:{}",s); //这一步只输出结果,不会给future.get();方法有返回值
                });
        future.get();
        log.info("我是主线程2");
    }

控制台打印:

[:10.79.8.110:7001] [e4cb0c4a8aa7d767,] 2025-10-14 11:09:43.635 INFO 48552 [http-nio-7001-exec-2] com.xxxx.TestController 我是主线程1
[:10.79.8.110:7001] [e4cb0c4a8aa7d767,] 2025-10-14 11:09:43.664 INFO 48552 [task-1] com.xxxx.TestController 最终数据结果为:计算结果: 300
[:10.79.8.110:7001] [e4cb0c4a8aa7d767,] 2025-10-14 11:09:43.665 INFO 48552 [http-nio-7001-exec-2] com.xxxx.TestController 我是主线程2

5、组合两个独立任务:thenCombine()
代码如下:

    @GetMapping("/test5")
    public void testMethod5() throws Exception {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(
        () -> {
            try {
                Thread.sleep(7000l);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("我是异步线程1");
            return 10;
        }, executor);

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(
        () -> {
            try {
                Thread.sleep(5000l);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("我是异步线程2");
            return 20;
        }, executor);
        CompletableFuture<Integer> result = future1.thenCombine(future2, (a, b) -> a + b);
        log.info("我是组合结果:{}", result.get());
    }

打印结果如下:

[:10.79.8.110:7001] [2a219e11c4e4e246,] 2025-10-14 11:20:18.383 INFO 71148 [task-2] com.xxxx.TestController 我是异步线程2
[:10.79.8.110:7001] [2a219e11c4e4e246,] 2025-10-14 11:20:20.383 INFO 71148 [task-1] com.xxxx.TestController 我是异步线程1
[:10.79.8.110:7001] [2a219e11c4e4e246,] 2025-10-14 11:20:20.384 INFO 71148 [http-nio-7001-exec-2] com.xxxx.TestController 我是组合结果:30

6、等待所有任务完成 allOf方法
代码 如下:

    //等待所有任务完成 allOf方法
    @GetMapping("/test6")
    public void testMethod6() throws Exception {
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(5000l);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("我是异步方法1");
        }, executor);

        CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(3000l);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("我是异步方法2");
        }, executor);
        CompletableFuture<Void> result = CompletableFuture.allOf(future1, future2);
        result.get();
        log.info("所有任务都完成");
    }

打印结果如下:

[10.79.8.110:7001] [2d8187cf09af6ce1,] 2025-10-14 11:38:28.203 INFO 60896 [task-2] com.xxxx.TestController 我是异步方法2
[10.79.8.110:7001] [2d8187cf09af6ce1,] 2025-10-14 11:38:30.211 INFO 60896 [task-1] com.xxxx.TestController 我是异步方法1
[10.79.8.110:7001] [2d8187cf09af6ce1,] 2025-10-14 11:38:30.213 INFO 60896 [http-nio-7001-exec-1] com.xxxx.TestController 所有任务都完成

7、处理异常:exceptionally()
代码如下:

    //处理异常:exceptionally()
    @GetMapping("/test7")
    public void testMethod7() throws Exception {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(
        () -> {
                    if (true) {
                        throw new RuntimeException("任务执行失败!");
                    }
                    return "正常结果";
                },executor).exceptionally(ex -> {
                    log.error("捕获异常:{}",ex.getMessage());
                    return "默认结果";  // 异常时返回默认值
                });

        log.info("最终输出结果:{}",future.get());
    }

输出结果如下

[:10.79.8.110:7001] [c12586e9a2cfe98d,] 2025-10-14 11:39:09.297 ERROR 60896 [task-3] com.xxxx.TestController 捕获异常:java.lang.RuntimeException: 任务执行失败!
[:10.79.8.110:7001] [c12586e9a2cfe98d,] 2025-10-14 11:39:09.299 INFO 60896 [http-nio-7001-exec-3] com.xxxx.TestController 最终输出结果:默认结果
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值