巧用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鎖的限制,性能上不存在問題。