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的根本实践和实际,这自身就是十分好用的两个类,心愿咱们在工作中能多多应用,不过也不要自觉应用多线程,因为多应用一种工具,就会多一份危险。