CompletableFuture使用样例

简介: CompletableFuture是对Future的扩展和增强。CompletableFuture实现了Future接口,并在此基础上进行了丰富的扩展,完美弥补了Future的局限性,同时CompletableFuture实现了对任务编排的能力。借助这项能力,可以轻松地组织不同任务的运行顺序、规则以及方式。从某种程度上说,这项能力是它的核心能力# 系列文章目录

CompletableFuture使用样例


前言

CompletableFuture是对Future的扩展和增强。CompletableFuture实现了Future接口,并在此基础上进行了丰富的扩展,完美弥补了Future的局限性,同时CompletableFuture实现了对任务编排的能力。借助这项能力,可以轻松地组织不同任务的运行顺序、规则以及方式。从某种程度上说,这项能力是它的核心能力# 系列文章目录


一、故事

  • 老爷和两个管家
  • 老爷想喝茶
  • 让两个管家协助
  • 一个烧水
  • 一个选茶叶
  • 自己去上厕所
  • 回来就可以喝茶
  • 岂不乐哉???

二、使用前用到的api

  • CompletableFuture 默认使用的是内部线程池,可自定义线程池使用
  • CompletableFuture.supplyAsync 创建异步调用任务
  • future.thenAccept 执行完成后通知

表示第一个任务执行完成后,执行第二个回调方法任务,会将该任务的执行结果,作为入参,传递到回调方法中,并且回调方法是有返回值的。

  • thenRun/thenRunAsync

通俗点讲就是,做完第一个任务后,再做第二个任务,第二个任务也没有返回值

  • CompletableFuture.allOf 编排异步任务 - 全部成功后通知thenAccept, 类似创建一个异步监听
  • future.join() 主线程会等待
  • supplyAsync 执行任务,支持返回值。
  • runAsync 执行任务,没有返回值。

三、直接上代码

管家需要做的两件事

@Service
public class TeaService {

    //烧茶水
    public Integer heatUpWater(){
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 20;
    }

    //挑选茶
    public Double getTeas(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 20D;
    }
}

老爷的吩咐

@Service
@Slf4j
public class QueryService {

    @Autowired
    private TeaService teaService;

    public Map<String,Object> queryGoodsInfo(){
        Map<String, Object> res = new HashMap<>();

        //异步执行任务 烧水
        log.info("老李,去帮我把水烧一下,一会我要喝茶");
        CompletableFuture<Integer> numFuture = CompletableFuture.supplyAsync(() -> teaService.heatUpWater());

        numFuture.thenAccept((result) -> {
            log.info("老爷水烧好了,花了{}分钟",result);
            res.put("烧水时间", result);
        }).exceptionally((e) ->{
            log.error("锅烧炸了: {}", e.getMessage(), e);
            res.put("烧水时间", null);
            return null;
        });


        //异步执行任务 挑选茶叶
        log.info("小蓉,去帮我挑选一下茶叶");
        CompletableFuture<Double> priceFuture = CompletableFuture.supplyAsync(() -> teaService.getTeas());
        priceFuture.thenAccept((result) ->{
            log.info("老爷,茶叶挑选好了,先试试{}克的碧螺春",result);
            res.put("挑选茶叶时间", result);
        }).exceptionally((e) ->{
            log.error("茶被老鼠吃了: {}", e.getMessage(), e);
            res.put("挑选茶叶时间", null);
            return null;
        });

        //编排任务allOf  全部等待完成
        CompletableFuture<Void> allQuery = CompletableFuture
            .allOf(numFuture, priceFuture);
        //执行完成后回调
        CompletableFuture<Map<String, Object>> future = allQuery.thenApply((result) -> {
            log.info("------------------ 老爷不放心,看一下监控,发现都弄好了 ------------------ ");
            return res;
        }).exceptionally((e) -> {
            log.error(e.getMessage(), e);
            return null;
        });
        log.info("我去上个厕所,弄好了给我发消息,一会回来喝茶");
        // --这里主线程会等待异步线程全部执行完毕
        // --去掉future.join() 主线程不等待
        future.join();
        log.info("开始喝茶...");
        return res;
    }

}

老爷的任务开始执行

@Controller
@Slf4j
@RequestMapping("/completable")
public class CompletableFutureController {

    @Autowired
    private QueryService queryService;

    @RequestMapping("get")
    @ResponseBody
    public Map<String,Object> get(){
        return queryService.queryGoodsInfo();
    }

}

老爷任务执行结果

[nio-8080-exec-2] c.e.w.completableFuture.QueryService     : 老李,去帮我把水烧一下,一会我要喝茶
[nio-8080-exec-2] c.e.w.completableFuture.QueryService     : 小蓉,去帮我挑选一下茶叶
[nio-8080-exec-2] c.e.w.completableFuture.QueryService     : 我去上个厕所,弄好了给我发消息,一会回来喝茶
[onPool-worker-2] c.e.w.completableFuture.QueryService     : 老爷,茶叶挑选好了,先试试20.0克的碧螺春
[onPool-worker-1] c.e.w.completableFuture.QueryService     : ------------------ 老爷不放心,看一下监控,发现都弄好了 ------------------ 
[onPool-worker-1] c.e.w.completableFuture.QueryService     : 老爷水烧好了,花了20分钟
[nio-8080-exec-2] c.e.w.completableFuture.QueryService     : 开始喝茶...

总结

  • 不建议使用默认线程池
CompletableFuture代码中又使用了默认的「ForkJoin线程池」,处理的线程个数是电脑「CPU核数-1」。在大量请求过来的时候,处理逻辑复杂的话,响应会很慢。一般建议使用自定义线程池,优化线程池配置参数。
  • CompletableFuture 功能很多,异步任务编排很灵活,有很多值得学习,有时间可以看看学习下,一定能解决工作中的很多问题
  • 获取返回值

    join()和get()方法都是用来获取CompletableFuture异步之后的返回值
    get 会有检查异常,需要手动处理(try catch)
    join 方法抛出的是uncheck异常(即RuntimeException),不会强制开发者抛出,处理
  • get和join 都是阻塞的方法
如果使用它来获取异步调用的返回值,需要添加超时时间。
CompletableFuture.get(5, TimeUnit.SECONDS);
目录
相关文章
|
6月前
CompletableFuture
CompletableFuture
25 0
|
7月前
|
Java 数据处理 数据库
CompletableFuture 使用
CompletableFuture 使用
38 0
|
8月前
|
消息中间件 Java 中间件
Future and CompletableFuture
Future代表异步执行的结果,也就是说异步执行完毕后,结果保存在Future里, 我们在使用线程池submit()时需要传入Callable接口,线程池的返回值为一个Future,而Future则保存了执行的结果 ,可通过Future的get()方法取出结果,如果线程池使用的是execute(),则传入的是Runnable接口 无返回值。
37 0
Zp
【CompletableFuture】CompletableFuture中join()和get()方法的区别
【CompletableFuture】CompletableFuture中join()和get()方法的区别
Zp
536 0
|
Java 测试技术
CompletableFuture使用详解
CompletableFuture是jdk8的新特性。CompletableFuture实现了CompletionStage接口和Future接口
230 0
CompletableFuture使用详解
|
Java 测试技术 Linux
CompletableFuture调用OpenFegin,谁用谁傻
当调用FeignBlockdingloadBalancerClient使用并行流(多线程)会有问题。
264 0
CompletableFuture调用OpenFegin,谁用谁傻
|
消息中间件 Dubbo Kafka
CompletableFuture学习
前面我们已经知道CompletionService是可以解决Future带来的阻塞问题的,同时我们除了前面我们看到的take方法之外,还可以使用poll方法,这样可以使你的程序免受阻塞之苦。因为poll方法也是无阻塞性的。同时在kafka的源码中,我们如果使用消费者的话,可以看到会使用一个基于future的poll方法。同时我们可以在dubbo的新版本2.7中,可以看到其异步编程采用的就是我们要介绍的CompletableFuture。因此,我们有必要了解CompletableFuture,同时其也是真正意义上的异步编程的实现。
116 0
CompletableFuture学习
|
关系型数据库 MySQL
CompletableFuture的使用案例
CompletableFuture的使用案例
191 0
两个例子解释Callable 和 Future接口
两个例子解释Callable 和 Future接口

热门文章

最新文章