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

2020-09-04 17:00:49 +08:00
 RedBeanIce

如下代码所示,我使用 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
      }
  }
  ```
3734 次点击
所在节点    Java
29 条回复
isir1234
2020-09-05 15:33:30 +08:00
runAsync 后加上异常处理试试

比如 CompletableFuture.runAsync(()->xxx).exceptionally(e -> {
// print exception here
return null;
}))
RedBeanIce
2020-09-06 01:06:42 +08:00
@zhady009
@mango88
@cs419
@Narcissu5
@coldear
@allan888
@isir1234

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

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

1,也没有报错的 log
2,
RedBeanIce
2020-09-06 01:11:36 +08:00
@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
2020-09-06 01:12:11 +08:00
#13
同样也会丢很多张,,,详情代码就是在上面链接代码里面,加了一个方法
amiwrong123
2020-09-06 11:16:56 +08:00
个人怀疑,是不是 ForkJoinPool#commonPool()的坑,难道是在 supplyAsync 内部提交 task 给 commonPool 的时候执行了什么奇怪的拒绝策略。

建议使用 supplyAsync(Supplier<U> supplier, Executor executor),自己给一个线程池,排除一下线程池的原因。
cs419
2020-09-06 12:41:42 +08:00
测了下代码 下载数量正确
代码贴在语雀下面了
你再试下,不行就让你同事也运行下
没准是你环境的问题
RedBeanIce
2020-09-06 13:24:18 +08:00
@cs419

#26 大佬我试了一下您的方案,还是不行,不过我在 QQ 群朋友的帮助下解决了。
https://www.yuque.com/docs/share/4ba58651-ac10-46ae-9175-1c5b43ec97ec?#
RedBeanIce
2020-09-06 13:33:15 +08:00
此贴 end

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

aHR0cHM6Ly93d3cueXVxdWUuY29tL2RvY3Mvc2hhcmUvNGJhNTg2NTEtYWMxMC00NmFlLTkxNzUtMWM1YjQzZWM5N2VjPyM=

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

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

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

总结:多线程好难啊!!!!
RedBeanIce
2020-09-06 13:34:24 +08:00
各位大佬,谢谢指教!!!!太强了

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

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

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

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

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

© 2021 V2EX