CompletableFuture FutureTask 的错误使用案例

2022-05-10 10:21:52 +08:00
 byte10

一、背景

在一些接口响应时间的优化中有一些任务是可以异步处理的,常常会用到这个线程池。但是有一些业务需要依赖异步任务的结果,那么就需要用到 CompletableFuture 或者 FutureTask ,或者在一些多任务并发执行的时候,CompletableFuture 可以提供一些方法让我们更容易的控制并发编程。

二、常见的错误操作

这种就是画蛇添足的做法,等于脱裤子放屁。

第二种方式是把一些业务代码放在 get()方法之前去执行。这种是 oK 的,但是还有一个问题。如果异步任务是 IO 密集型,那么就需要解决线程池的问题。

第三种:使用额外的线程池,处理这个 IO 密集型任务。

总结

使用 CompletableFuture 、FutureTask 的前提:

  1. 你的业务有异步的需求且需要知道任务执行的结果。比如是否执行完成,执行结果。
  2. 你的整个业务链中有不依赖这个异步任务结果的业务代码,这样就可以先执行这些中间业务代码。
  3. 这个中间业务代码( IO 阻塞或者非阻塞代码,非阻塞代码一般很快)的执行时间 W ,W 最好大于或者等于这个异步任务的执行时间。如果 W 执行很快,比如是非阻塞代码,那么这个异步任务的意义不大。

注意事项:

  1. 如果这个异步任务是一个非阻塞代码(也就是计算密集型代码),那么直接用即可(底层是 ForkJoinPool)。这个在就不再阐述原理了,在线程池那集有讲过。
  2. 如果这个异步任务是一个 IO 阻塞型代码,那么就需要使用一个额外的自定义线程池去处理,这个线程池的大小取决于这个任务的 IO 和 cpu 时间比,还有和中间任务的时间比,大致确定范围即可。

这些问题的详细描述在我的 B 站里 ,java 并发编程: https://www.bilibili.com/video/BV1CA4y1S7Pe? 这些都是 API 的使用,其实很无聊。但是错的人太多了,所以就讲一下这个问题。头大。。

其他的场景就是多个任务同步执行的情况,可以使用 allOf ,compose 等,那么就不在这里给大家演示了。

1451 次点击
所在节点    Java
4 条回复
byte10
2022-05-11 14:02:04 +08:00
这么经典没人看,难道大家都这么强的吗 😂 。
golangLover
2022-05-12 09:13:21 +08:00
我觉得你这个写法其实也没解决核心问题。很多时候业务的代码是有上下依赖的,正确的做法是用 cf 的 chainning 例如 thenAccept 和 thenapplu 之类,最后直接等待这个 cf.
byte10
2022-05-12 09:42:04 +08:00
@golangLover 嗯 你要关注前提条件,首先用 CompletableFuture ,FutureTask 必须是有依赖结果的,否则直接用线程池异步处理就可以了。 我的讲解是:非依赖异步任务的中间业务,可以放在 get() 方法前面。有依赖异步任务结果的业务,那必须放在 get()后面。如果不存在 这样的中间业务代码,那么不要用 CompletableFuture ,请直接用线程池代替。

当然如果有多个异步任务并行处理,CompletableFuture 还是很划算的,方便处理。

另外你说的这个 thenAccept/whenComplete 并不能进行同步编程,不能满足业务需求,这一点非常的重要,一定要看到本质。先看需求,再看实现的方式,然后在看本质,如果看不到本质,就用侧代码去验证它。你说的那些方式只不过是实现需求的一种方案,本质还是线程池,你可以验证下,其实都一样。你一定要验证,同样都能完成一样的业务需求,不同的写法是否能给你带来的性能的提升。
golangLover
2022-05-12 13:25:01 +08:00
我只能讲用途有限吧,不过你的说话是没错的。至于直接用线程池,我不会这么做

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/851906

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX