巧用CompletableFuture返回值解决性能瓶颈
需求背景
对一组字符串数据进行处理,处理逻辑简单封装一个对象,包装该字符串,最终汇总返回。
下面实现只使用CompleteableFuture,其它方式如paraStream不考虑。
无返回值CompletableFuture实现
考虑到性能,对于字符串的处理可以进行并发。实现如下:
private static ExecutorService executorService = Executors.newFixedThreadPool(10,
UserThreadFactory.build("future-test"));
private List<String> result = new ArrayList<>();
@Before
public void init() {
for (int i = 0; i < 1000; i++) {
result.add("a" + i);
}
}
@Test
public void testCompleteFuture1(){
List<List<String>> partition = Lists.partition(result, 100);
Vector<AD> vector=new Vector<>();
List<CompletableFuture<Void>> futures = partition.stream().map(list -> {
return CompletableFuture.runAsync(() -> {
for (String s : list) {
AD ad = new AD();
ad.name = s + "--p";
vector.add(ad);
}
}, executorService);
}).collect(toList());
CompletableFuture.allOf(futures.toArray(new CompletableFuture[]{})).join();
}
利用了CompletableFuture<Void>进行并发处理,使用线程安全的vector用于汇聚最后的数据。上文的存在的问题是线程安全的vector有锁导致性能降低,实现应用中逻辑比上述例子复杂,该处成为了性能瓶颈。
有返回值CompletableFuture实现
使用带有返回值的completable编码实现
public void testCompleteFuture2() {
List<List<String>> partition = Lists.partition(result, 100);
List<CompletableFuture<List<AD>>> futures = partition.stream().map(list -> {
return CompletableFuture.supplyAsync(() -> {
List<AD> ads = new ArrayList<>();
for (String s : list) {
AD ad = new AD();
ad.name = s + "--p";
ads.add(ad);
}
return ads;
}, executorService);
}).collect(toList());
List<AD> collect = futures.stream().map(p -> {
try {
return p.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return null;
}).filter(Objects::nonNull).flatMap(List::stream).collect(toList());
}
上述的例子则没了vector锁的限制,性能上不存在问题。