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