在回答这个问题,我注意到一个奇怪的现象CompletableFuture
:如果你有一个CompletableFuture cf
和链的呼叫cf.exceptionally()
,呼叫cf.get()
出现运行异常:
exceptionally()
块返回前ExecutionException
我缺少的东西,或这是一个错误?我在Ubuntu 17.04使用Oracle JDK 1.8.0_131。
下面的代码说明了这一现象:
public static void main(String[] args) {
long start = System.currentTimeMillis();
final CompletableFuture<Object> future = CompletableFuture.supplyAsync(() -> {
sleep(1000);
throw new RuntimeException("First");
}).thenApply(Function.identity());
future.exceptionally(e -> {
sleep(1000);
logDuration(start, "Exceptionally");
return null;
});
final CompletableFuture<Void> futureA = CompletableFuture.runAsync(() -> {
try {
future.get();
} catch (Exception e) {
} finally {
logDuration(start, "A");
}
});
final CompletableFuture<Void> futureB = CompletableFuture.runAsync(() -> {
sleep(1100);
try {
future.get();
} catch (Exception e) {
} finally {
logDuration(start, "B");
}
});
try {
future.join();
} catch (Exception e) {
logDuration(start, "Main");
}
futureA.join();
futureB.join();
}
private static void sleep(final int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private static void logDuration(long start, String who) {
System.out.println(who + " waited for " + (System.currentTimeMillis() - start) + "ms");
}
输出:
B waited for 1347ms
Exceptionally waited for 2230ms
Main waited for 2230ms
A waited for 2230ms
正如你所看到的,futureB
这之前调用睡一点get()
都不会阻止。然而,无论是futureA
和主线程等待exceptionally()
完成。
请注意,如果您删除此行为不会发生.thenApply(Function.identity())
。
唤醒休眠的线程是具有像任何其他要处理的依赖性作用,它不具有优先级。在另一方面,一个线程轮询CompletableFuture
时,它已经完成不会进入睡眠状态,没有必要被唤醒,因此,没有必要竞争与其他相关行动。
与以下程序
public static void main(String[] args) {
final CompletableFuture<Object> future = CompletableFuture.supplyAsync(() -> {
waitAndLog("Supplier", null, 1000);
throw new RuntimeException("First");
}).thenApply(Function.identity());
long start = System.nanoTime();
CompletableFuture.runAsync(() -> waitAndLog("A", future, 0));
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(10));
future.exceptionally(e -> {
waitAndLog("Exceptionally", null, 1000);
return null;
});
CompletableFuture.runAsync(() -> waitAndLog("B", future, 0));
CompletableFuture.runAsync(() -> waitAndLog("C", future, 1100));
waitAndLog("Main", future, 0);
ForkJoinPool.commonPool().awaitQuiescence(10, TimeUnit.SECONDS);
}
private static void waitAndLog(String msg, CompletableFuture<?> primary, int sleep) {
long nanoTime = System.nanoTime();
Object result;
try {
if(sleep>0) Thread.sleep(sleep);
result = primary!=null? primary.get(): null;
} catch (InterruptedException|ExecutionException ex) {
result = ex;
}
long millis=TimeUnit.NANOSECONDS.toMillis(System.nanoTime()-nanoTime);
System.out.println(msg+" waited for "+millis+"ms"+(result!=null? ", got "+result: ""));
}
我得到的,
Supplier waited for 993ms
A waited for 993ms, got java.util.concurrent.ExecutionException: java.lang.RuntimeException: First
C waited for 1108ms, got java.util.concurrent.ExecutionException: java.lang.RuntimeException: First
Exceptionally waited for 998ms
Main waited for 1983ms, got java.util.concurrent.ExecutionException: java.lang.RuntimeException: First
B waited for 1984ms, got java.util.concurrent.ExecutionException: java.lang.RuntimeException: First
我的机器上,这表明在这个特定的情况下,相关的行动是对的,他们在原定的顺序执行A
第一。请注意,我插入额外的等待时间调度之前Exceptionally
,这将是未来相关的动作。由于B
在后台线程运行时,它是不确定性是否管理之前安排自己Main
的线程或不。我们可以在前面插入另一延迟强制执行命令。
由于C
民意调查已完成的未来,它可以立即进行,因此其净等待时间接近明确指定的睡眠时间。
必须强调的是,这是一个特别的情景只有结果,依赖于实现细节。有没有保证相关的操作,执行顺序。正如你可能已经注意到自己,没有.thenApply(Function.identity())
一步,实施运行,导致有关动作的不同执行顺序不同的代码路径。
的依赖性形成树和实现必须遍历它以有效的方式,而不用担心堆栈溢出,因此它具有以某种方式与小的变化,以将其压平的依赖关系树中的非可影响所得到的顺序的形状直观的方式。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句