01-CompletableFuture异步线程 入门
前言
emmm, 有好长一段时间没有发布新的文章了,最后一篇发布还是1月21日,到现在已经快3个月了,这段时间我去干嘛了呢?
1: 学习数据结构与算法, 但是还没有学完,打算等学习完毕后再给大家分享
2: 学习Java 9 - 15的新特性, 应为关注Java的发展方向这一块来说,基本是每个学习Java的人员所必备的, 为啥没有16和17呢?应为我在网上没有找到好的视频,所以等以后找到了在学习而且新特特性应该不是很多,但是9-17加起来就很多了
3: 就是最近这段时间我离职了, 在交接和找新的工作,所很大一部分精力都用在了这个上面
但是今天给大家带来了一个新的东西,这个东西用的人也比较少, 其实也不是很新,是Java8中的一个类而已,但是非常好用, 用过多线程的人应该都知道,线程的创建,回收,管理,池化,都很难弄, 但是学习了这个东西, 妈妈再也不用担心我用不好多线程了
CompletableFuture是什么
从名称看来和Future有关,没错,他也是Future的实现,和FutureTask平级,也是用来实现异步线程任务的,并且携带返回值, 具体的使用直接从需求出发,关注下面的需求和实现, 即可掌握
需求
小白来餐厅吃饭, 点了一盘番茄炒蛋+米饭,小白开始打王者,厨师开始炒菜,小白开吃
需求点: 厨师需要单独的线程
实现
编写代码
package com.dance; import org.junit.jupiter.api.Test; import java.util.StringJoiner; import java.util.concurrent.CompletableFuture; public class CompletableFutureTest { @Test public void testOne(){ print("小白进入餐厅"); print("小白点了 番茄炒蛋 + 一碗米饭"); CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> { print("厨师炒菜"); sleep(200); print("厨师打饭"); sleep(100); return "番茄炒蛋 + 米饭 好了"; }); print("小白在打王者"); print(String.format("%s , 小白开吃", cf1.join())); } /** * 休眠方法 * @param millis 毫秒 */ public static void sleep(long millis){ try { Thread.sleep(millis); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 打印方法 * @param text 文本 */ public static void print(String text){ String str = new StringJoiner("\t|\t") .add(String.valueOf(System.currentTimeMillis())) .add(String.valueOf(Thread.currentThread().getId())) .add(Thread.currentThread().getName()) .add(text) .toString(); System.out.println(str); } }
执行结果
1649430128924 | 1 | main | 小白进入餐厅 1649430128924 | 1 | main | 小白点了 番茄炒蛋 + 一碗米饭 1649430128926 | 1 | main | 小白在打王者 1649430128927 | 24 | ForkJoinPool.commonPool-worker-19 | 厨师炒菜 1649430129134 | 24 | ForkJoinPool.commonPool-worker-19 | 厨师打饭 1649430129244 | 1 | main | 番茄炒蛋 + 米饭 好了 , 小白开吃
厨师用单独的线程去干活了, 异步线程,如此简单
需求进化
在餐厅中一般厨师都只负责炒菜,像打饭这样的事情都是交给服务员来的
需求点:厨师炒完菜后交给服务员,服务员新开线程去打饭
实现
编写代码
@Test public void testTwo(){ print("小白进入餐厅"); print("小白点了 番茄炒蛋 + 一碗米饭"); CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> { print("厨师炒菜"); sleep(200); return "番茄炒蛋"; }).thenCompose(dis -> CompletableFuture.supplyAsync(() -> { print("服务员打饭"); sleep(100); return dis + " + 米饭 好了"; })); print("小白在打王者"); print(String.format("%s , 小白开吃", cf1.join())); }
执行结果
1649431094323 | 1 | main | 小白进入餐厅 1649431094324 | 1 | main | 小白点了 番茄炒蛋 + 一碗米饭 1649431094326 | 1 | main | 小白在打王者 1649431094326 | 24 | ForkJoinPool.commonPool-worker-19 | 厨师炒菜 1649431094538 | 24 | ForkJoinPool.commonPool-worker-19 | 服务员打饭 1649431094645 | 1 | main | 番茄炒蛋 + 米饭 好了 , 小白开吃
按道理来说这里应该是两个线程的,但是估计我这个执行的太快了,所以后面的任务也提交给了这个线程,我感觉这种代码调用流程就很清晰,看着像Promise
需求进化
小白进入餐厅的时候,开始点菜,要一盘番茄炒蛋+米饭, 但是这个时候米饭是没有蒸好的,需要开始去蒸,所以厨师炒菜,服务员去蒸饭,这两个事情应该是同时进行的,在厨师炒完菜,服务员蒸好饭,厨师将菜交给服务员,服务员打饭,交给小白,小白吃饭
需求点: 厨师炒菜和服务员蒸饭需要同时进行,并且是厨师炒完菜交给服务员
实现
编写代码
@Test public void testThree(){ print("小白进入餐厅"); print("小白点了 番茄炒蛋 + 一碗米饭"); CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> { print("厨师炒菜"); sleep(200); return "番茄炒蛋"; }).thenCombine(CompletableFuture.supplyAsync(() -> { print("服务员开始蒸饭"); sleep(300); return "米饭"; }), (dis, rice) -> { print("服务员打饭"); sleep(100); return String.format("%s + %s , 好了", dis, rice); }); print("小白在打王者"); print(String.format("%s , 小白开吃", cf1.join())); }
执行结果
1649431885256 | 1 | main | 小白进入餐厅 1649431885256 | 1 | main | 小白点了 番茄炒蛋 + 一碗米饭 1649431885259 | 1 | main | 小白在打王者 1649431885259 | 24 | ForkJoinPool.commonPool-worker-19 | 服务员开始蒸饭 1649431885259 | 25 | ForkJoinPool.commonPool-worker-5 | 厨师炒菜 1649431885571 | 24 | ForkJoinPool.commonPool-worker-19 | 服务员打饭 1649431885681 | 1 | main | 番茄炒蛋 + 米饭 , 好了 , 小白开吃
这里出现了两个线程,正好可以看到
总结
方法名 |
描述 |
supplyAsync |
用来开启一个异步任务 |
thenCompose |
用来连接两个有依赖关系的任务,结果由最后一个返回 |
thenCombine |
用来合并两个任务,结果由函数(BiFunction)返回 |
第一个任务的需求点在于线程的开启
第二个任务的需求点在于两个线程的连接
第三个任务的需求点在于两个线程的结果合并
怎么样到这里是不是已经简单的入门了呢
作者:彼岸舞
时间:2022\04\11
内容关于:CompeletableFuture
本文来源于网络,只做技术分享,一概不负任何责任