判断Java 的 Future 为啥还没结果
在诊断Java应用的时候, 我们经常看到很多异步的调用, 当前线程等待其它线程返回结果. 有时候会发现这些线程迟迟没有往下走, 于是查看线程状态, 会看到如下的线程栈:
sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
java.util.concurrent.FutureTask.awaitDone(FutureTask.java:429)
java.util.concurrent.FutureTask.get(FutureTask.java:191)
com.tianxiaohui.RboCache.compute(RboCache.java:46)
我们会很自然的发问, 为什么它停在这, 到底发生了什么?
初步分析
对于这种情况, 有2种情况:
- 这个任务还没有被执行, 所以没有结果;
- 这个任务在执行中, 所以还没有结果;
对于第一种情况, 要去查看线程为什么没启动, 或者线程池为啥没执行它. 通常这时候要查看线程池的core size, max size, 和workQueue 和 workers 的情况.
对于第二种情况, 则要查看执行的线程在干什么事情, 为什么还没出结果.
java.util.concurrent.Future
对于java.util.concurrent.Future
我们很容易的能从它的字段: runner
看出谁在执行, 还是没有在执行, 如下图:
java.util.concurrent.CompletableFuture
对于 java.util.concurrent.CompletableFuture
则稍微麻烦一些, 要看谁指向它:
- 如果指向它的对象除了当前线程还有另外一个线程, 那么另外一个线程就是执行线程;
- 如果指向它的对象除了当前线程还有一个等待队列中的某个对象, 则它还没开始执行;
上面用到的代码:
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Supplier;
public class WhyFutureStuck {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(2);
Future<String> f1 = es.submit((Callable<String>) new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(3600 * 1000);
return "result-f1";
}
});
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync((Supplier<String>) () -> {
try {
Thread.sleep(3600 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "result-cf1";
}, es);
//threads are used up
Future<String> f2 = es.submit((Callable<String>) new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(3600 * 1000);
return "result-f2";
}
});
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync((Supplier<String>) () -> {
try {
Thread.sleep(3600 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "result-cf2";
}, es);
try {
System.out.println(f1.get());
System.out.println(f2.get());
System.out.println(cf1.get());
System.out.println(cf2.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}