一文讲清楚CompletableFuture,优化项目代码,提高查询效率

本文介绍了在项目中如何通过使用CompletableFuture进行异步编程优化查询效率,探讨了supplyAsync和runAsync方法的区别,并提供了实际代码示例。
摘要由CSDN通过智能技术生成

前言

  CompletableFuture我很少用,但不排除要用到的情况。我个人觉得挺鸡肋的(可能是我业务场景有限制)。在成本够/项目大的情况我们可能会用队列。单体项目中我们会对一些接口进行拆分,用事件监听加异步的方式去实现。

为什么会用到异步编程

   在一些单体项目中用户登录,注册后会查询一些用户信息组合后返回,比如基本信息,会员等级对应的权限,会员等级可以提供的优惠有哪些,根据用户点击记录推送一下喜欢的活动等(如果需要的话啊,登录后立马展示这种)。伪代码如下:

@Test
public void login() throws InterruptedException {
	StopWatch stopWatch = new StopWatch();
	stopWatch.start("task-name:登录用户!");
	//用户登录-返回一些头像...基本信息
	Thread.sleep(1000l);
	stopWatch.stop();
	//查询用户权限对应的菜单...菜单
	stopWatch.start("task-name:查询用户权限对应的菜单!");
	Thread.sleep(2000l);
	stopWatch.stop();
	System.out.println("stopWatch: " + stopWatch.prettyPrint());
	System.out.println("总耗时:"+stopWatch.getTotalTimeMillis());
	//组合菜单和基本信息到同一个对象并进行返回
	//return;.... 等
}

我们看下打印结果

stopWatch: StopWatch '': running time = 3014869800 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
1009126200  033%  task-name:登录用户!
2005743600  067%  task-name:查询用户权限对应的菜单!

总耗时:3014

那么如何优化呢?我们先尝试最基本写法来优化。(简单使用)

首先我们先将上面的代码更改一下,将StopWatch用来记录整个方法耗时。
@Test
    public void login2() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("task-name:整个耗时!");
        CompletableFuture<Map<String, Object>> infoFuture = CompletableFuture.supplyAsync(() -> {
            try{
                //用户登录-返回一些头像...基本信息
                Thread.sleep(1000l);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            //在这里模拟一下基本信息并返回
            Map<String, Object> infoMap = new HashMap();
            infoMap.put("name", "张三");
            infoMap.put("age", 18);
            return infoMap;
        });
        //查询用户权限对应的菜单...菜单
        CompletableFuture<Map<String, Object>> menuFuture = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000l);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Map<String, Object> menuMap = new HashMap<String, Object>();
            menuMap.put("menu", "首页,账户管理,积分管理");
            return menuMap;
        });
        Map<String, Object> infoJoin = infoFuture.join();
        Map<String, Object> menuJoin = menuFuture.join();
        stopWatch.stop();
        System.out.println("stopWatch: " + stopWatch.prettyPrint());
        System.out.println("总耗时:"+stopWatch.getTotalTimeMillis());
        //组合菜单和基本信息到同一个对象并进行返回
        //return;.... 等
    }

打印结果

stopWatch: StopWatch '': running time = 2007791800 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
2007791800  100%  task-name:整个耗时!

总耗时:2007

第一节结果

  我们可以看到同样的sleep时间,采用CompletableFuture后时间少了一些。

讲解

   在上面的方法中,我们用到了CompletableFuture的两个方法。
第一个是: CompletableFuture的类的静态函数supplyAsync
  该函数通过异步方式执行supplier提供的任务,并返回一个CompletableFuture对象,用于处理任务的结果。
第二个是: 方法join()
  当调用 join() 方法时,如果 CompletableFuture 已经完成了计算,则直接返回结果;否则,它会阻塞当前线程直到计算完成。
  如果 CompletableFuture 在完成时异常终止,join() 方法会抛出一个未经检查的异常(CompletionException),该异常封装了原始异常作为其原因。如果是由于取消而导致的异常,则会抛出 CancellationException。
  相比于 get() 方法,join() 不会抛出 InterruptedException,因此在主线程不希望被中断的情况下,join() 可能是更方便的选择。但是,无论 join() 还是 get(),当 CompletableFuture 被取消或执行过程中抛出了异常,都会阻止程序继续执行,只是它们处理异常的方式略有不同。

第二节:深入了解

  我们接着继续往下看,我们在上一阶段中使用了supplyAsync创建了异步线程。
其实在使用CompletableFuture创建异步任务时,一般有supplyAsync和runAsync两个方法:

创建异步任务的supplyAsync和runAsync

supplyAsync执行CompletableFuture任务,支持返回值。
runAsync执行CompletableFuture任务,没有返回值。

具体代码我们可点开这个类的源码查看。

//supplyAsync 方法
//使用默认内置线程池ForkJoinPool.commonPool(),根据supplier构建执行任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
//自定义线程,根据supplier构建执行任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

//runAsync 方法
//使用默认内置线程池ForkJoinPool.commonPool(),根据runnable构建执行任务
public static CompletableFuture<Void> runAsync(Runnable runnable) 
//自定义线程,根据runnable构建执行任务
public static CompletableFuture<Void> runAsync(Runnable runnable,  Executor executor)

我直接上代码讲和注释,大家可以cv到idea上运行就明白。

supplyAsync


    @Test
    public void defaultSupplyAsync() {
        //有返回值,该方法内部使用自己类中默认线程池对象
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() ->{
            try {
                Thread.sleep(5000l);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            return "complete";
        });
        String result = completableFuture.join();//阻塞,等待返回结果
        log.info("方法执行完毕!");
        System.out.println("result:" + result);
    }
	
    @Test
    public void customSupplyAsync(){
        //用自己的线程池
       ExecutorService executorService = Executors.newFixedThreadPool(10);
       CompletableFuture<String> callback = CompletableFuture.supplyAsync(()->{
            try{
                Thread.sleep(5000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
                return "complete";
        }, executorService);
        String result = callback.join();
        System.out.println("result: "+result);
    }

runAsync

    @Test
    public void defaultRunAsync(){
        //采用这种方式,没有返回值,该方法内部使用自己类中默认线程池对象
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(5000l);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            log.info("方法执行完毕!");
        });
        completableFuture.join();
    }

    @Test
    public void customRunAsync(){
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        CompletableFuture.runAsync(()->{
            try{
                Thread.sleep(5000l);
            }catch (InterruptedException exception){
                exception.printStackTrace();
            }
        }, executorService);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值