跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • CompletableFuture 使用交流(其实是困解)
未分類
7 9 月 2020

CompletableFuture 使用交流(其实是困解)

CompletableFuture 使用交流(其实是困解)

資深大佬 : RedBeanIce 0

如下代码所示,我使用 CompletableFuture 进行多线程的下载

但是我的 map 里面有 29 个图片 URL,我只得到了 26 张,所以求助大佬们,

1,我的代码哪里有问题

2,求助完整的 CompletableFuture 的使用方式

 private static void downloadCompletableFuture(Map<String, String> map) {       try {           List<CompletableFuture<Void>> futureList = new ArrayList<>();           for (Map.Entry<String, String> stringStringEntry : map.entrySet()) {               // image Url               String imageUrl = stringStringEntry.getValue();               CompletableFuture<Void> future = CompletableFuture.runAsync(new Runnable() {                   @Override                   public void run() {                       // download picture                       DownloadPicture3.download(imageUrl);                   }               });               futureList.add(future);           }           CompletableFuture<Void> allDoneFuture =             CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]));           allDoneFuture.get(20, TimeUnit.SECONDS);       } catch (Exception e) {           e.printStackTrace();       } finally {           log.info("end");           // 11:27:37.442 [main] INFO com.ice.http.JucDownloadPicture       }   }   ``` 

大佬有話說 (29)

  • 資深大佬 : AllanAG

    既然使用了 CompletableFuture,最好使用异步的方式完成整个流程。
    1 图片下载不够,推测是超时时间太短,allDoneFuture.get(20, TimeUnit.SECONDS);20s 执行时间不够
    2 可以下面那段代码修改成这种方式试试
    “`
    CompletableFuture<Void> allDoneFuture =
    CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]));
    allDoneFuture.whenCompleteAsync((void1, void2) -> {
    // 所有完成回调
    log.info(“end”);
    });
    “`

  • 主 資深大佬 : RedBeanIce

    @AllanAG
    #1 谢谢!!!!!!我现在去试试。

  • 主 資深大佬 : RedBeanIce

    @AllanAG

    #1 实际上不行,whenCompleteAsync 虽然是在获得结果完成后执行,但是实际上,一张图片也没有,log 也没有打印

    “`
    private static void downloadCompletableFuture2(Map<String, String> map) {
    try {
    List<CompletableFuture<Void>> futureList = new ArrayList<>();
    for (Map.Entry<String, String> stringStringEntry : map.entrySet()) {
    // image Url
    String imageUrl = stringStringEntry.getValue();
    CompletableFuture<Void> future = CompletableFuture.runAsync(new Runnable() {
    @Override
    public void run() {
    // download picture
    log.info(“下载所花时间 = ” + DownloadPicture3.download(imageUrl));
    }
    });
    futureList.add(future);
    }
    CompletableFuture<Void> allDoneFuture = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]));
    allDoneFuture.whenCompleteAsync((void1, void2) -> {
    // 所有完成回调
    log.info(“================================end”);
    });

    } catch (Exception e) {
    e.printStackTrace();
    } finally {
    log.info(“end”);
    // 11:27:37.442 [main] INFO com.ice.http.JucDownloadPicture
    }
    }
    “`

  • 資深大佬 : wysnylc

    CompletableFuture 使用交流(其实是困解)收集的资料

  • 資深大佬 : putaozhenhaochi

    用有返回结果的方法控制看看

  • 資深大佬 : mango88

    @RedBeanIce

    CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])).join();

    阻塞等待所有的完成

  • 主 資深大佬 : RedBeanIce

    @wysnylc
    #4 图裂开。

  • 主 資深大佬 : RedBeanIce

    @mango88

    #6 不行,仍然少了三张

    CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])).join();

  • 主 資深大佬 : RedBeanIce

    @putaozhenhaochi
    #5 求推荐,我已经人傻了。。。。。

  • 資深大佬 : mango88

    @RedBeanIce

    少三张,可能就不是并行的原因了,也许是下载报错了吧

  • 主 資深大佬 : RedBeanIce

    @mango88
    #6

    https://imgchr.com/i/wk77B6

  • 主 資深大佬 : RedBeanIce

    @mango88
    #10

    我同时使用一个普通方法下载,然后使用的 completablefuture 下载,一前一后执行,,前面的普通方法还是 29 张,但是一到后面这个就少了 2-3 张,每次执行不等。

  • 資深大佬 : putaozhenhaochi

    @RedBeanIce 哈哈 CompletableFuture 不熟

    要不试试 Stream 并行:
    map.values().stream().parallel().forEach( v->{
    System.out.println(v);
    });

  • 資深大佬 : cheng6563

    Java 这几个 API 实在太复杂了

  • 資深大佬 : zhady009

    用这个都要加个 exceptionally(tx -> {log…})
    不然你找不出问题

  • 資深大佬 : mango88

    奇怪了,看起来没啥问题。DownloadPictutre 方法能简单贴一下吗 ?

  • 資深大佬 : cs419

    排查手段不详细
    既然下载有缺失
    那把那 3 个失败的单独跑一遍代码是什么结果 (排除这 3 个链接就有问题)
    再针对下载成功的 26 个链接,下载 50 次 又是啥结果 (排除任务太多)

    在方法 DownloadPicture3.download(imageUrl); 前后打印序号
    确保 download 方法 成功的确完成了 50 次调用
    如果这里没毛病,那就是 download 方法内部执行出了问题

    获取结果 allDoneFuture.get() 不使用超时时间
    allDoneFuture.exceptionally(e->{
    System.out.println(“出错-“+ e.getMessage());
    e.printStackTrace();
    });
    打印报错

  • 資深大佬 : Narcissu5

    “`java
    DownloadPicture3.download(imageUrl);
    “`

    比较可能是这个方法有线程安全的问题。另外如果 IO 不是异步的,使用异步就没意义。如果 IO 是异步的,那么这个方法本身就应该返回的是 Future,不需要在 runAsync 。另外 runAsync 本身会使用`ForkJoinPool#commonPool()`,而 ForkJoinPool 里面是不能放异步方法的,你这样子可能把整个 JVM 都拖慢

  • 資深大佬 : coldear

    add more logs to debug.

  • 資深大佬 : allan888

    download 之前 random 的等几秒钟试试?有可能有的网站图片会防止太多并发的连接。
    大概这样:
    Thread.sleep(ThreadLocalRandom.current().nextInt(0, 15000));
    DownloadPicture3.download(imageUrl);

  • 資深大佬 : isir1234

    runAsync 后加上异常处理试试

    比如 CompletableFuture.runAsync(()->xxx).exceptionally(e -> {
    // print exception here
    return null;
    }))

  • 主 資深大佬 : RedBeanIce

    @zhady009
    @mango88
    @cs419
    @Narcissu5
    @coldear
    @allan888
    @isir1234

    https://www.yuque.com/docs/share/61f38a49-764c-4b6e-9271-53e06fc0d32d?#

    各位大佬代码已经贴出来了,,大佬们可以在自己的电脑执行链接中的代码,
    大佬们的方法我都试了一下,好像不行

    1,也没有报错的 log
    2,

  • 主 資深大佬 : RedBeanIce

    @putaozhenhaochi

    private static void downloadStream(Map<String, String> map) throws IOException {
    map.values().parallelStream().forEach(new Consumer<String>() {
    @SneakyThrows
    @Override
    public void accept(String s) {
    Long download = download(s);
    System.out.println(download);
    }
    });
    }

  • 主 資深大佬 : RedBeanIce

    #13
    同样也会丢很多张,,,详情代码就是在上面链接代码里面,加了一个方法

  • 資深大佬 : amiwrong123

    个人怀疑,是不是 ForkJoinPool#commonPool()的坑,难道是在 supplyAsync 内部提交 task 给 commonPool 的时候执行了什么奇怪的拒绝策略。

    建议使用 supplyAsync(Supplier<U> supplier, Executor executor),自己给一个线程池,排除一下线程池的原因。

  • 資深大佬 : cs419

    测了下代码 下载数量正确
    代码贴在语雀下面了
    你再试下,不行就让你同事也运行下
    没准是你环境的问题

  • 主 資深大佬 : RedBeanIce

    @cs419

    #26 大佬我试了一下您的方案,还是不行,不过我在 QQ 群朋友的帮助下解决了。
    https://www.yuque.com/docs/share/4ba58651-ac10-46ae-9175-1c5b43ec97ec?#

  • 主 資深大佬 : RedBeanIce

    此贴 end

    下面贴代码(上面的是大佬的方案,下面是测试 3 次的代码,可行) base64

    aHR0cHM6Ly93d3cueXVxdWUuY29tL2RvY3Mvc2hhcmUvNGJhNTg2NTEtYWMxMC00NmFlLTkxNzUtMWM1YjQzZWM5N2VjPyM=

    错误原因:错误的使用 long startTime = System.currentTimeMillis() 作为文件的名字,文件被覆盖了

    解决措施:使用 AtomicLong.incrementAndGet()自增,原子性的增加然后返回的操作

    另外:LongAdder 由于没有 incrementAndGet,所以只能 increment(),然后 longvalue(),这样不是原子的操作,所以也会覆盖(中间有一个版本,我没有使用 System.currentTimeMillis(),使用了 LongAdder 仍然失败了)

    总结:多线程好难啊!!!!

  • 主 資深大佬 : RedBeanIce

    各位大佬,谢谢指教!!!!太强了

    贴一下 26 的大佬
    https://www.yuque.com/docs/share/6065b121-4732-4826-bac2-3bb356f0461e?#

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具