我有一些任务,可以使用多线程以最有效的方式聚合来自多个链接的一些信息。链接位于某个阵列。现在,我有这样的解决方案:
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秒。我可以加快速度吗?
答案 0 :(得分:1)
使用以下代码:
List<String> resultList = Arrays.stream(link).parallel().map(v -> doSomeJobWithLink(v)).collect(Collectors.toList());
通常我们避免在流管道中尝试捕获,但是如果您必须捕获异常,请阅读Exception Handling in Java Streams
不要仅仅因为可以而使用parallel
,因为额外的开销会使您的工作比没有parallel
的工作花费更多的时间
答案 1 :(得分:1)
不确定下面的代码是否会提高性能,但是我认为这是解决问题的更干净的方法。
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());
答案 2 :(得分:1)
当您执行Collections.synchronizedList(new ArrayList<>())
时,您将synchronized
放在整个列表上,即列表上的任何操作都共享相同的互斥量甚至读取,这会带来较高的性能损失,并且是限制因素。
更好的方法是只收集到正常列表,Collector保证无序并发缩减。
对于并发收集器,实现可以自由(但不是必需)并发实现缩减。并发归约是使用相同的可同时修改的结果容器从多个线程并发调用累加器函数的一种方法,而不是在累加过程中保持隔离的结果。仅当收集器具有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());
尽管不建议将异常吞没,除非这种情况是不可避免的。