V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
byte10
V2EX  ›  Java

CompletableFuture FutureTask 的错误使用案例

  •  
  •   byte10 · 2022-05-10 10:21:52 +08:00 · 1283 次点击
    这是一个创建于 689 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一、背景

    在一些接口响应时间的优化中有一些任务是可以异步处理的,常常会用到这个线程池。但是有一些业务需要依赖异步任务的结果,那么就需要用到 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 等,那么就不在这里给大家演示了。

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

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

    另外你说的这个 thenAccept/whenComplete 并不能进行同步编程,不能满足业务需求,这一点非常的重要,一定要看到本质。先看需求,再看实现的方式,然后在看本质,如果看不到本质,就用侧代码去验证它。你说的那些方式只不过是实现需求的一种方案,本质还是线程池,你可以验证下,其实都一样。你一定要验证,同样都能完成一样的业务需求,不同的写法是否能给你带来的性能的提升。
    golangLover
        4
    golangLover  
       2022-05-12 13:25:01 +08:00 via Android
    我只能讲用途有限吧,不过你的说话是没错的。至于直接用线程池,我不会这么做
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2905 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 13:58 · PVG 21:58 · LAX 06:58 · JFK 09:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.