我有一些任务,以最有效的方式从多个链接聚合一些信息,使用多线程。链接在某个数组上。现在,我有了这样的解决方案:
Arrays.stream(link).parallel().forEach(link -> {
try {
String result = doSomeJobWithLink(link);
System.out.println(result);
} catch (IOException e) {
e.printStackTrace();
}
});而且它运行得很好(工作已经完成了2秒)。
但是我不想在try块中打印结果,而是在某个列表(或其他集合)中收集结果,所以我这样做了:
List<String> resultList = Collections.synchronizedList(new ArrayList<>());
Arrays.stream(link).parallel().forEach(link -> {
try {
String result = doSomeJobWithLink(link);
resultList.add(result);
} catch (IOException e) {
e.printStackTrace();
}
});
resultList.forEach(System.out::println);但只花了5-8秒而不是2秒。我能加快速度吗?
发布于 2019-12-30 09:45:00
使用以下代码:
List<String> resultList = Arrays.stream(link).parallel().map(v -> doSomeJobWithLink(v)).collect(Collectors.toList());通常,我们避免在流管道中尝试捕获,但是如果必须捕获异常,请阅读Java流中的异常处理。
不要仅仅因为你可以使用parallel,因为额外的开销,你的工作需要更多的时间而不用parallel
发布于 2019-12-30 10:11:49
当您这样做时,Collections.synchronizedList(new ArrayList<>())会在整个列表中放置一个synchronized,也就是说,列表上的任何操作都共享相同的互斥对象,甚至读取,这是一个高性能的代价,也是限制因素。
一个更好的方法是只收集到一个正常的列表,收集器保证一个无序的并发缩减。
对于并发收集器,实现可以(但不需要)并发实现约简。并发缩减是指从多个线程并发调用累加器函数,使用相同的并发可修改的结果容器,而不是在累积期间保持结果隔离。只有当收集器具有Collector.Characteristics.UNORDERED特性或原始数据无序时,才应应用并发还原。
因此,以下内容将显著提高性能,
List<String> resultList = Arrays.stream(link).parallel().map(e -> {
try {
return doSomeJobWithLink(e);
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
return result;
}).filter(Objects::nonNull).collect(Collectors.toList());虽然它不建议吞咽例外,除非这是不可避免的。
发布于 2019-12-30 09:50:21
不确定下面的代码是否会提高性能,但我认为这将是解决问题的一种更干净的方法。
List<String> resultList = Arrays.stream(link).parallel().map(e -> {
String result = null;
try {
result = doSomeJobWithLink(e);
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
return result;
}).filter(e -> e != null).collect(Collectors.toList());https://stackoverflow.com/questions/59528630
复制相似问题