在工作中发现了一个有意思的Bug,这里记录一下,大致的逻辑代码如下:
public Object outMethod(){
List<CompletableFuture<Object>> futures = new ArrayList<>(10);
for(int i=0;i<10;i++){
CompletableFuture<Object> future = CompletableFuture.supplyAsync(() -> {
Object data = interMethod(); //关键方法
return data;
});
futures.add(future);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); // 3
}
public Object interMethod() {
List<CompletableFuture<Object>> futures = new ArrayList<>(10);
for (int i = 0;i<10 i++) {
CompletableFuture<Object> future = CompletableFuture.supplyAsync(() -> {
// some logic
return object;
});
futures.add(future);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
}
在执行outMethod
方法中调用了 interMethod
然后在 interMethod
中又使用到了CompletableFuture,我们知道CompletableFuture的supplyAsync()方法是将任务提交给ForkJoinPool.commonPool()
线程池去执行的。
上面代码产生死锁的原因是:ForkJoinPool.commonPool()的线程数默认是 CPU数量-1,在OutMethod执行的过程中可能会消耗完所有的线程数,然后阻塞在 //3步上,等待任务执行完成,但是在执行内部的interMethod()
此时线程池中没有线程能执行,导致interMethod
被阻塞,等待outMethod()方法执行完毕,释放线程,而outMethod方法需要interMethod方法执行完毕才会返回结果释放线程,所以outMethod方法和interMethod形成了循环等待就形成了死锁。
解决方案:定义两个不同的线程池,分别执行对应的outMethod和interMethod即可。