SpringBoot异步Async使用Future与CompletableFuture区别小结

 更新时间:2023年06月09日 09:51:46   作者:可乐汉堡cola  
本文主要介绍了SpringBoot异步Async使用Future与CompletableFuture区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

主要区别:

  • Future:在执行结束后没法回调,调用get方法会被阻塞;
  • CompletableFuture:在执行结束后可通过whenComplete或whenCompleteAsync方法回调,不会阻塞线程,同时也是支持get方法的;

代码示例

spring boot配置Async,@EnableAsync启动异步

AsyncConfig

package com.test.config;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@EnableAsync
@Configuration
public class AsyncConfig {
    /**
     * 异步任务自定义线程池
     */
    @Bean(name="taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(50);
        executor.setMaxPoolSize(500);
        executor.setQueueCapacity(300);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("自定义线程-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        return executor;
    }
}

1.Future测试:

主线程等待各个异步执行的线程返回的结果来做下一步操作,则必须阻塞在future.get()的地方等待结果返回,这时候又变成同步了。适用于需要等异步结果的场景。

FutureService

package com.test.service;
import java.util.concurrent.Future;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
@Service
public class FutureService {
    @Async
    public Future<String> futureTest1(){
        System.out.println(Thread.currentThread().getName()+"进行任务futureTest1...");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"任务futureTest1完成");
        return new AsyncResult<String>("这是任务futureTest1返回结果");
    }
    @Async
    public Future<String> futureTest2(){
        System.out.println(Thread.currentThread().getName()+"进行任务futureTest2...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"任务futureTest2完成");
        return new AsyncResult<String>("这是任务futureTest2返回结果");
    }
}

FutureController

package com.test.controller;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.test.service.FutureService;
@RestController
@RequestMapping("/future")
public class FutureController {
    @Autowired
    private FutureService futureService;
    //超时时间
      public static final long timeout = 30;
    @RequestMapping(value = "futureTest", method = RequestMethod.GET)
    public String futureTest() {
        // 开始时间戳
        long beginTime = System.currentTimeMillis();
        Future<String> result1 = futureService.futureTest1();
        Future<String> result2 = futureService.futureTest2();
        //添加结果集,30秒超时
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            String str1 = result1.get(timeout, TimeUnit.SECONDS);
            System.out.println(str1);
            String str2 = result2.get(timeout, TimeUnit.SECONDS);
            System.out.println(str2);
            map.put("result1", str1);
            map.put("result2", str2);
            //这里需要等get()完成后才会执行,因为get()方法会阻塞
            System.out.println("map集合: "+map.size());
            System.out.println("回调后的任务: "+Thread.currentThread().getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("耗时: "+(System.currentTimeMillis() - beginTime));
        return "success";
    }
}

 打印结果

自定义线程-1进行任务futureTest1...
自定义线程-2进行任务futureTest2...
自定义线程-1任务futureTest1完成
这是任务futureTest1返回结果
自定义线程-2任务futureTest2完成
这是任务futureTest2返回结果
map集合: 2
回调后的任务: http-nio-8082-exec-1
耗时: 5068

大家可以看到,这时候map集合里面是有值的,主线程http-nio-8082-exec-1是在异步执行完才执行的,因为get方法是会阻塞线程的。耗时5秒是以异步中耗时最长的方法为准,因为要等耗时最长的方法执行完,才能合并。

2.CompletableFuture测试:

实现了Future和CompletionStage接口,保留了Future的优点,并且弥补了其不足。即异步的任务完成后,需要用其结果继续操作时,无需等待。适用于不需要等异步结果的场景。

CompletableFutureService

package com.test.service;
import java.util.concurrent.CompletableFuture;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class CompletableFutureService {
    @Async
    public CompletableFuture<String> completableFuture1(){
        System.out.println(Thread.currentThread().getName()+"进行任务completableFuture1...");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"任务completableFuture1完成");
        return CompletableFuture.completedFuture("这是任务completableFuture1返回结果");
    }
    @Async
    public CompletableFuture<String> completableFuture2(){
        System.out.println(Thread.currentThread().getName()+"进行任务completableFuture2...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"任务completableFuture2完成");
        return CompletableFuture.completedFuture("这是任务completableFuture2返回结果");
    }
}

CompletableFutureController

package com.test.controller;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.test.service.CompletableFutureService;
@RestController
@RequestMapping("/completable")
public class CompletableFutureController {
    @Autowired
    private CompletableFutureService completableFutureService;
    @RequestMapping(value = "completableFutureTest", method = RequestMethod.GET)
    public String CompletableFutureTest() {
        // 开始时间戳
        long beginTime = System.currentTimeMillis();
        CompletableFuture<String> result1 = completableFutureService.completableFuture1();
        CompletableFuture<String> result2 = completableFutureService.completableFuture2();
        //添加结果集,30秒超时
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            result1.whenComplete((r, t)->{
                System.out.println(r+Thread.currentThread().getName());
                map.put("result1", r);
            });
            result2.whenComplete((r, t)->{
                System.out.println(r+Thread.currentThread().getName());
                map.put("result2", r);
            });
            //这里不用等前面的结果集,会异步先执行
            System.out.println("map集合: "+map.size());
            System.out.println("回调后的任务: "+Thread.currentThread().getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("耗时: "+(System.currentTimeMillis() - beginTime));
        return "success";
    }
}

打印结果

map集合: 0
回调后的任务: http-nio-8082-exec-1
耗时: 33
自定义线程-1进行任务completableFuture1...
自定义线程-2进行任务completableFuture2...
自定义线程-1任务completableFuture1完成
这是任务completableFuture1返回结果自定义线程-1
自定义线程-2任务completableFuture2完成
这是任务completableFuture2返回结果自定义线程-2

大家可以看到,这时候map集合里面是空的,主线程http-nio-8082-exec-1是在异步之前打印的,说明使用whenComplete是异步的,不会阻塞线程的。耗时33毫秒不用等异步执行完就能打印。

这里简单说下whenComplete和whenCompleteAsync的区别:

whenComplete:执行完当前任务的线程,继续执行 whenComplete 的任务。
whenCompleteAsync: 执行完当前任务的线程,把whenCompleteAsync 的任务继续提交给线程池来执行。(可能开启新的线程)

把前面的改成whenCompleteAsync测试一下

result1.whenCompleteAsync((r, t)->{
                System.out.println(r+Thread.currentThread().getName());
                map.put("result1", r);
            });
            result2.whenCompleteAsync((r, t)->{
                System.out.println(r+Thread.currentThread().getName());
                map.put("result2", r);
            });

打印结果

map集合: 0
回调后的任务: http-nio-8082-exec-1
耗时: 33
自定义线程-1进行任务completableFuture1...
自定义线程-2进行任务completableFuture2...
自定义线程-1任务completableFuture1完成
这是任务completableFuture1返回结果Thread-4
自定义线程-2任务completableFuture2完成
这是任务completableFuture2返回结果Thread-5

区别的地方在于Thread-4和Thread-5,这是新开的线程,不是线程池中的线程了。

总结:

Future与CompletableFuture使用场景不一样,都支持get方法,如果异步执行完后需要同步,使用Future,反之,如果异步执行完后,不需要等待,直接异步操作,那么使用CompletableFuture。

到此这篇关于SpringBoot异步Async使用Future与CompletableFuture区别小结的文章就介绍到这了,更多相关SpringBoot Future CompletableFuture内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java基本事件处理机制解析

    java基本事件处理机制解析

    这篇文章主要介绍了java基本事件处理机制解析, Java事件处理机制是一种用于处理用户交互和系统事件的编程模型,它基于事件驱动的思想,通过监听和响应事件来实现程序的交互性和动态性,需要的朋友可以参考下
    2023-10-10
  • 使用Mybatis遇到的there is no getter异常

    使用Mybatis遇到的there is no getter异常

    这篇文章主要介绍了使用Mybatis遇到的there is no getter异常,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • Springmvc 4.x利用@ResponseBody返回Json数据的方法

    Springmvc 4.x利用@ResponseBody返回Json数据的方法

    这篇文章主要介绍了Springmvc 4.x利用@ResponseBody返回Json数据的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • Netty学习教程之Netty与Marshalling结合发送对象

    Netty学习教程之Netty与Marshalling结合发送对象

    Netty是由JBOSS提供的一个Java开源框架,之前已经给大家简单介绍了一些基础与使用,下面这篇文章主要给大家介绍了关于Netty与Marshalling结合发送对象的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-05-05
  • Java多线程饥饿与公平介绍及代码示例

    Java多线程饥饿与公平介绍及代码示例

    这篇文章主要介绍了Java多线程饥饿与公平介绍及代码示例,分析饥饿产生的原因以及相关实例,然后就在java中实现公平性问题做了详细解析,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • Spring Boot使用RestTemplate消费REST服务的几个问题记录

    Spring Boot使用RestTemplate消费REST服务的几个问题记录

    这篇文章主要介绍了Spring Boot使用RestTemplate消费REST服务的几个问题记录,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • 详解Spring Boot实战之Restful API的构建

    详解Spring Boot实战之Restful API的构建

    这篇文章主要介绍了详解Spring Boot实战之Restful API的构建,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • SpringBoot Data JPA 关联表查询的方法

    SpringBoot Data JPA 关联表查询的方法

    这篇文章主要介绍了SpringBoot Data JPA 关联表查询的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • Java生成二维码可添加logo和文字功能

    Java生成二维码可添加logo和文字功能

    这篇文章主要介绍了Java生成二维码可添加logo和文字功能,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-02-02
  • Struts2学习笔记(9)-Result配置全局结果集

    Struts2学习笔记(9)-Result配置全局结果集

    这篇文章主要介绍Struts2中使用Result配置全局结果集的方法,希望能给大家做一个参考。
    2016-06-06

最新评论