24 | CompletableFuture:异步编程没那么难
该思维导图由 AI 生成,仅供参考
CompletableFuture 的核心优势
- 深入了解
- 翻译
- 解释
- 总结
CompletableFuture是Java 1.8版本引入的工具类,用于支持异步编程,其核心优势在于简化了多线程编程的复杂性。通过CompletableFuture,可以轻松实现任务的并行化和串行化,并清晰地描述任务之间的时序关系。文章首先介绍了异步编程的基本概念,然后通过烧水泡茶的例子展示了CompletableFuture的使用优势。接着详细介绍了如何创建CompletableFuture对象以及如何理解CompletionStage接口。CompletionStage接口可以描述串行关系、AND聚合关系、OR聚合关系以及异常处理,通过thenApply、thenAccept、thenRun和thenCompose等系列方法实现。文章通过示例代码清晰地展示了这些方法的使用。总的来说,CompletableFuture为异步编程提供了强大的支持,简化了多线程编程的复杂性,使得任务的并行化和串行化变得更加清晰和简洁。文章还提到了异常处理和异步编程的发展趋势,以及对RxJava项目的推荐。文章内容丰富,对异步编程有很好的介绍和指导作用。
《Java 并发编程实战》,新⼈⾸单¥59
全部留言(79)
- 最新
- 精选
- J.M.Liu思考题: 1.没有进行异常处理, 2.要指定专门的线程池做数据库查询 3.如果检查和查询都比较耗时,那么应该像之前的对账系统一样,采用生产者和消费者模式,让上一次的检查和下一次的查询并行起来。 另外,老师把javadoc里那一堆那一堆方法进行了分类,分成串行、并行、AND聚合、OR聚合,简直太棒了,一下子就把这些方法纳入到一个完整的结构体系里了。简直棒
作者回复: 思考题考虑的很全面👍
2019-04-234144 - 袁阳思考题: 1,读数据库属于io操作,应该放在单独线程池,避免线程饥饿 2,异常未处理
作者回复: 👍👍
2019-04-237118 - 密码123456我在想一个问题,明明是串行过程,直接写就可以了。为什么还要用异步去实现串行?
作者回复: 这个简单场景没必要用
2019-04-23549 - 发条橙子 。老师 ,我有个疑问。 completableFuture 中各种关系(并行、串行、聚合),实际上就覆盖了各种需求场景。 例如 : 线程A 等待 线程B 或者 线程C 等待 线程A和B 。 我们之前讲的并发包里面 countdownLatch , 或者 threadPoolExecutor 和future 就是来解决这些关系场景的 , 那有了 completableFuture 这个类 ,是不是以后有需求都优先考虑用 completableFuture ?感觉这个类就可以解决前面所讲的类的问题了
作者回复: 我觉得可以优先使用CompletableFuture,当然前提是你的jdk是1.8
2019-04-24245 - 青莲1.查数据库属于io操作,用定制线程池 2.查出来的结果做为下一步处理的条件,若结果为空呢,没有对应处理 3.缺少异常处理机制
作者回复: 👍👍
2019-04-2322 - 笃行之”如果所有 CompletableFuture 共享一个线程池,那么一旦有任务执行一些很慢的 I/O 操作,就会导致线程池中所有线程都阻塞在 I/O 操作上,从而造成线程饥饿,进而影响整个系统的性能。”老师,阻塞在io上和是不是在一个线程池没关系吧?
作者回复: 有关系,如果系统就一个线程池,里面的线程都阻塞在io上,那么系统其他的任务都需要等待。如果其他任务有自己的线程池,就没有问题。
2019-04-29217 - J.M.Liu我觉得既然都讲到CompletableFuture了,老师是不是有必要不一章ForkJoinPool呀?毕竟,ForkJoinPool和ThreadPoolExecutor还是有很多不一样的。谢谢老师
作者回复: 后面有介绍
2019-04-2312 - henry老师我现在有个任务,和您的例子有相似的地方,是从一个库里查询多张表的数据同步到另外一个库,就有双重for循环,最外层用与多张表的遍历,内层的for循环用于批量读取某一张表的数据,因为数据量可能在几万条,我想分批次读出来再同步到另一个数据库,昨天写的时候用的是futuretask,今天正好看到老师的文章就改成了CompletableFuture,还没有用异常处理的,后面我还要看看怎么加上异常处理的。其它的不知道我用的对不对,请老师看看: // 初始化异步工具类,分别异步执行2个任务 CompletableFuture<List<PBSEnergyData>> asyncAquirePBSEnergyData = new CompletableFuture(); CompletableFuture<List<AXEEnergyData>> asyncSaveAxeEnergyData = new CompletableFuture(); // 初始化两个线程池, 分别用于2个任务 ,1个任务一个线程池,互不干扰 Executor aquirePBSEnergyDataExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); Executor saveAxeEnergyDataExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); queryUtils.getTableNames().forEach(tableName -> { int pageSize = queryUtils.getPageSize(); //查询该表有多少条数据,每${pageSize}条一次 int count = pbsEnergyService.getCount(tableName); //总页数 int pages = count / pageSize; int pageNum = 0; final int pageNo = pageNum; for(pageNum = 0; pageNum <= pages; pageNum++){ // 异步获取PBS数据库的数据并返回结果 asyncAquirePBSEnergyData .supplyAsync(() -> { 查询数据库 return pbsEnergyDatas; },aquirePBSEnergyDataExecutor) // 任务2任务1,任务1返回的结果 .thenApply(pbsEnergyDatas -> asyncSaveAxeEnergyData.runAsync(()->{ List<AXEEnergyData> axeEnergyDatas = pbsEnergyDatas.stream().map(pbsEnergyData -> { //进行类型转换 }).collect(Collectors.toList()); //批量保存 },saveAxeEnergyDataExecutor)); } }); 全部贴上去,超过字符数了,只能请老师凑合看了 :(
作者回复: 有个地方需要注意:runAsync和supplyAsync都是静态方法。 线程池设置的太小了,这是个IO密集型的任务 thenApply里面的runAsync我觉得好像是没有必要,增加了复杂的了。 如果thenApply里面需要异步,可以用thenApplyAsync
2019-04-24411 - Chocolate回答「密码123456」:CompletableFuture 在执行的过程中可以不阻塞主线程,支持 runAsync、anyOf、allOf 等操作,等某个时间点需要异步执行的结果时再阻塞获取。
作者回复: 是的,复杂场景就能体现出优势了
2019-04-2349 - MondayCompletableFuture从来没玩过,老师在工作/实践中有使用过这个类吗?
作者回复: 用过,配合lambda效果很好
2019-12-237