关于java:JAVA并发编程Future和CompletableFuture的简介与使用

1.Future类简介

2.Future类的应用

3.CompletableFuture类的简介

4.CompletableFuture类的的应用

5.总结

1.Future类简介
咱们之前在这篇文章 JAVA并发编程——Callable接口和FutureTask简介和应用中说过,Future就是能够取得返回值的一个异步解决线程类。
在之前无论是继承Thread类还是实现Runnable接口,咱们始终无奈失去线程的返回值,然而Future就能够,Future是领有返回值的线程,这样说咱们可能还不明确,咱们用一个生产的案例来阐明一下这个例子。

2.Future类的应用
假如咱们当初有这样一个需要:

咱们先来看一个页面:

这是一个网上找来的数据大屏的图片,如果这个屏幕的数据要你查问出并且返回,你会怎么做?

咱们晓得大屏展现的区域不同,要查问的业务逻辑也就不一样,能够将它分成如下这几块区域:

如果当初是一名老手,可能会一个区域一个区域串行地去查问,区域一查完了查区域二,紧接着区域三,而后再返回给前端,这样效率不高。

改良办法:多个区域同步查问,最初对立返回给前端(多箭齐发)。

这么说还是比拟形象,咱们用一张图片举例:

用这张图片应该就能解释了,接下来咱们应用Future类进行代码展现一下:

public DataResult queryData() throws ExecutionException, InterruptedException, TimeoutException {
      
        DataResult result = new DataResult();
        //查问区域1
        FutureTask<Integer> allHumanNum = new FutureTask<>(() -> queryHumanNum());
        //应用线程池执行
        CalculateThreadPoolConfig.getThreadPoolExecutor().submit(allDoctorNum);
        //查问区域2
        FutureTask<Integer> allMaleNum = new FutureTask<>(() -> queryAllMaleNum());
       //应用线程池执行 CalculateThreadPoolConfig.getThreadPoolExecutor().submit(queryInvitedDoctorNum);
       //顺次类推
       //......

        //拼接后果集
        result.setAllHumanNum(allHumanNum.get(5, TimeUnit.SECONDS));
        result.setAllMaleNum(allMaleNum.get(5, TimeUnit.SECONDS));
        return result;
    }

能够看出,咱们无论查问多少个数据集,只用破费查问工夫最长的那一个数据集的工夫就够了,就实现了同步并行查问的成果,大大减少查问工夫。

不过咱们要留神一下这行代码:

allHumanNum.get(5, TimeUnit.SECONDS);

get的意思是始终阻塞返回后果,然而加了一个数字和单位就代表,只期待5秒,不然会抛异样。
咱们工作的时候,如果须要调用他人的接口返回数据,然而不晓得对方要计算多久,可能对方一个接口就把咱们写的整个性能给拖垮。为了防止这种状况,工作中为了爱护本人,咱们须要学会加一层保险,如果对方的接口如果五秒没有返回就过时不候了。

3.CompletableFuture类的简介

介绍了后面的FutreTask类,咱们晓得这尽管是一个很好用的类,然而也是有很多的毛病的,比方:
1.get()容易阻塞
2.如果想要把多个后果汇合并成一个后果集,就须要很多FutureTask
3.FutureTask只能用于单步的简略计算,不能将两个异步计算合成一个异步计算,这两个异步计算相互独立,同时第二个又依赖第一个的后果。
4.当Future汇合中某个工作最快完结时,返回后果。
等等

毛病还是比拟多的,这个时候咱们就能够应用 CompletableFuture 类!

CompletableFuture补救了Future模式的毛病。它能够将多个后果汇合并成一个后果集,又能够将一个异步工作分成好几个阶段进行计算,前面的后果又依赖之前的后果等等。

总而言之,FutureTask能办到的事,CompletableFuture也能办到,同时CompletableFuture还能办到更多的事件!

咱们先搜寻一下CompletableFuture这个类

它实现了Future接口和CompletionStage接口。

CompletionStage:代表异步计算过程中的某一个阶段,一个阶段实现当前可能会触发另外一个阶段。

咱们认真看CompletionStage领有的办法,依据thenApply,whenComplete,exceptionally等办法,能够大略看出这个类能够对一个计算的每个阶段进行编程,先执行完一步,(依据thenApply)再执行下一步,(whenComplete)最初运行完了会怎么样,(exceptionally)抛出异样会怎么样等等。

如同就像一条调用链一样,这就是所谓的链式编程。

接下来咱们用一个案例用CompletableFuture进行实战。

4.CompletableFuture类的的应用

假如咱们当初有一个电商比价需要:
咱们要从各大网站上收集Think in java这本书的价格,开多个线程从各大网站收集,收集完之后输入信息到控制台,线程执行结束也要输入信息,请应用CompletableFuture和链式编程来进行解决。

咱们接下来开始编程:


public class CompletableFutureNextMallDemo {
 // 这是须要收集的价格网站
 static List<NetMall> list = Arrays.asList(
            new NetMall("jd"),
            new NetMall("pdff"),
            new NetMall("tmall"),
            new NetMall("xhs"),
            new NetMall("xy"),
            new NetMall("zh"),
            new NetMall("sf")
    );

}
class NetMall {
    // 这是网站的具体类以及计算价格的办法
    private String mallName;

    public NetMall() {
    }

    public NetMall(String mallName) {
        this.mallName = mallName;
    }

    public String getMallName() {
        return mallName;
    }

    public void setMallName(String mallName) {
        this.mallName = mallName;
    }

    //计算价格的办法
    public double calcPrice(String productName) {
        try {
            //假如这是计算过程
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0);
    }

}

最要害的执行办法

    //链式调度 多箭齐发
    public static List<String> getPriceByASync(List<NetMall> list, String productName) {

        //循环遍历所有网站,每一个网站开启一个线程进行计算
        //CompletableFuture.supplyAsync代表有返回值的执行过程,相当于Future的get(0办法
        return list.stream().map(netMall -> CompletableFuture.supplyAsync(() -> {
            return String.format(productName + " in %s price is %.2f", netMall.getMallName(), netMall.calcPrice(productName));
        }
        //当第一步执行结束后,第二步就是输入书的价格
        ).thenApply(r-> {
            System.out.println(r);
            return r;
        }
        //当实现工作之后,输入该网站计算实现
        ).whenComplete((v,e)-> {
            System.out.println(netMall.getMallName()+"计算实现!");
        }
        //当抛出异样的时候,输入异样
        ).exceptionally(e->{
            System.out.println("获取失败,出现异常!" );
            e.printStackTrace();
            return null;
        })).collect(Collectors.toList())
           .stream()
           //最初对立调用join办法返回后果
           .map(CompletableFuture::join)
           //最初合并成一个List,作为后果返回
           .collect(Collectors.toList());
    }

这里咱们将CompletableFuture抽离进去会发现代码会是如下所示:

CompletableFuture
.supplyAsync()//执行有返回值的异步解决,相当于FutureTask的get()
.thenApply()//接着执行第二步
.whenComplete()//当实现时触发的逻辑
.exceptionally();//当出现异常的时候触发的逻辑

5.总结

通过明天的学习,咱们理解了Future和CompletableFuture的根本实践和实际,这自身就是十分好用的两个类,心愿咱们在工作中能多多应用,不过也不要自觉应用多线程,因为多应用一种工具,就会多一份危险。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理