Future.get(long, TimeUnit)
java
/**
* Waits if necessary for at most the given time for the computation
* to complete, and then retrieves its result, if available.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
* @throws TimeoutException if the wait timed out
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
从方法签名看,会抛出三种异常,注释中又多出一种超时异常。
FutureTask.get(long, TimeUnit)和CompletableFuture.get(long, TimeUnit)都实现了Future.get(long, TimeUnit)方法,他们的异常行为是一致的
| 异常类型 | 是否受检 | 异常原因 |
|---|---|---|
| InterruptedException | 否 | 执行get的线程被中断(不是任务线程被中断) |
| TimeoutException | 否 | 任务执行超时 |
| CancellationException | 是 | 任务完成前被cancel |
| ExecutionException | 否 | 其他异常都会被包装成ExecutionException,真实异常通过getCause获取 |
如果使用了cancel操作,要记得处理CancellationException
java
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<String> future = executorService.submit(() -> {
Thread.sleep(100);
return "";
});
future.cancel(true);
try {
future.get(10, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
System.out.println("InterruptedException");
} catch (ExecutionException e) {
System.out.println("ExecutionException");
} catch (TimeoutException e) {
System.out.println("TimeoutException");
} catch (CancellationException e) {
System.out.println("CancellationException");
} finally {
executorService.shutdown();
}
}
捕获ExecutionException,并得到真实异常
java
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<String> future = executorService.submit(() -> {
throw new RuntimeException("Err Msg");
});
try {
future.get(10, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
System.out.println("InterruptedException");
} catch (ExecutionException e) {
System.out.println("ExecutionException " + e.getCause());
} catch (TimeoutException e) {
System.out.println("TimeoutException");
} catch (CancellationException e) {
System.out.println("CancellationException");
} finally {
executorService.shutdown();
}
}
捕获InterruptedException。捕获InterruptedException后,抛出InterruptedException的线程的中断标识会被重置,所以需要手动设置Thread.currentThread().interrupt();
java
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(1);
Thread mainThread = Thread.currentThread();
Future<String> future = executorService.submit(() -> {
Thread.sleep(1000);
return "ok";
});
// 中断执行get的线程
new Thread(() -> {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("mainThread before " + mainThread.isInterrupted());
mainThread.interrupt();
System.out.println("mainThread after " + mainThread.isInterrupted());
}).start();
try {
future.get(1000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
System.out.println("InterruptedException mainThread before " + mainThread.isInterrupted());
// InterruptedException会清除中断标记,生产代码通常需要恢复
Thread.currentThread().interrupt();
System.out.println("InterruptedException mainThread after " + mainThread.isInterrupted());
} catch (ExecutionException e) {
System.out.println("ExecutionException " + e.getCause());
} catch (TimeoutException e) {
System.out.println("TimeoutException");
} catch (CancellationException e) {
System.out.println("CancellationException");
} finally {
executorService.shutdown();
}
}
看一下java.util.concurrent.FutureTask#get(long,TimeUnit)
java
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
if (s <= COMPLETING &&
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
// 抛出超时异常
throw new TimeoutException();
return report(s);
}
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
// 抛出取消异常,如果此时异步任务已经完成,是不会进入到这里,也就不会抛出异常
throw new CancellationException();
// 其余异常都被包装成ExecutionException
throw new ExecutionException((Throwable)x);
}
说完了FutureTask.get(long, TimeUnit)再看下CompletableFuture#get(long, TimeUnit)
java
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
long nanos = unit.toNanos(timeout);
Object r;
if ((r = result) == null)
// 这里抛出TimeoutException
r = timedGet(nanos);
return (T) reportGet(r);
}
private static Object reportGet(Object r)
throws InterruptedException, ExecutionException {
if (r == null)
throw new InterruptedException();
if (r instanceof AltResult) {
Throwable x, cause;
if ((x = ((AltResult)r).ex) == null)
return null;
if (x instanceof CancellationException)
throw (CancellationException)x;
// 注意这里有CompletionException,但是没有直接抛出,而是包装成ExecutionException再抛出
if ((x instanceof CompletionException) &&
(cause = x.getCause()) != null)
x = cause;
throw new ExecutionException(x);
}
return r;
}
也举个例子
java
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(1);
Thread thread = Thread.currentThread();
CompletableFuture<Object> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "sssss";
}, executorService);
// 中断主线程
new Thread(() -> {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
thread.interrupt();
}).start();
try {
Object s = future.get(1000, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
System.out.println("ExecutionException");
} catch (InterruptedException e) {
System.out.println("InterruptedException");
} catch (TimeoutException e) {
System.out.println("TimeoutException");
} finally {
executorService.shutdown();
}
}
CompletableFuture.get(long,TimeUnit) VS CompletableFuture.join()
java
public T join() {
Object r;
if ((r = result) == null)
// join不会超时,这里不会抛出超时异常
r = waitingGet(false);
return (T) reportJoin(r);
}
private static Object reportJoin(Object r) {
if (r instanceof AltResult) {
Throwable x;
if ((x = ((AltResult)r).ex) == null)
return null;
// 这里还是会抛出CancellationException
if (x instanceof CancellationException)
throw (CancellationException)x;
// 这里抛出的不再是ExecutionException,而是CompletionException
if (x instanceof CompletionException)
throw (CompletionException)x;
// 其余异常包装成CompletionException
throw new CompletionException(x);
}
return r;
}
CompletableFuture.join()抛出异常分析
| 异常类型 | 是否受检 | 异常原因 |
|---|---|---|
| InterruptedException | 否 | join不会抛出InterruptedException |
| TimeoutException | 否 | join不会抛出TimeoutException |
| CancellationException | 是 | 任务完成前被cancel |
| ExecutionException | 否 | join不会抛出ExecutionException |
| CompletionException | 否 | 其他异常都会被包装成CompletionException,真实异常通过getCause获取 |
抛出CompletionException
java
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(1);
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("wwwww");
}, executorService);
try {
String s = future.join();
} catch (CompletionException e) {
System.out.println(e);
} finally {
executorService.shutdown();
}
}
抛出CancellationException
java
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(1);
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "";
}, executorService);
try {
future.cancel(true);
String s = future.join();
} catch (CancellationException e) {
System.out.println(e);
} catch (CompletionException e) {
System.out.println(e);
} finally {
executorService.shutdown();
}
}
CompletableFuture传播链及异常行为
传播链
可以把 CompletableFuture 理解成一条由多个"阶段(stage)"组成的流水线。每调用一次 thenApply、handle、exceptionally 等方法,通常都会创建并返回一个新的 CompletableFuture 对象。
java
CompletableFuture<Integer> f1 =
CompletableFuture.supplyAsync(() -> "123");
CompletableFuture<Integer> f2 =
f1.thenApply(String::length);
CompletableFuture<String> f3 =
f2.thenApply(length -> "长度:" + length);
每个节点都保存自己的完成状态:
- 未完成
- 正常完成
- 异常完成
- 被取消
上一个节点完成后,会触发依赖它的下一个节点。
返回的 Future 对象会变化吗
java
public static void main(String[] args) {
CompletableFuture<String> f1 =
CompletableFuture.completedFuture("hello");
CompletableFuture<Integer> f2 =
f1.thenApply(String::length);
CompletableFuture<String> f3 =
f2.thenApply(String::valueOf);
System.out.println(f1 == f3); // false
// java: 不可比较的类型: java.util.concurrent.CompletableFuture<java.lang.Integer>和java.util.concurrent.CompletableFuture<java.lang.String>
// System.out.println(f2 == f3);
}
可以理解为:f1 ──依赖关系──> f2 ──依赖关系──> f3
写成链式调用时:
java
CompletableFuture<String> future =
CompletableFuture.supplyAsync(() -> "hello")
.thenApply(String::length)
.thenApply(String::valueOf);
变量 future 最终只保存最后一个阶段的引用。前面的对象依旧存在,只是没有被单独保存到变量中。
中间发生异常时如何传递
java
CompletableFuture<String> future =
CompletableFuture.supplyAsync(() -> "hello")
.thenApply(value -> {
throw new IllegalStateException("转换失败");
})
.thenApply(value -> {
System.out.println("不会执行");
return value.toString();
});
传递过程如下:
supplyAsync:正常完成,结果为 "hello"
↓
第一个 thenApply:执行thenApply的Function ,返回一个异常结束状态的CompletableFuture,内部的异常被CompletionException包装
↓
第二个 thenApply:跳过thenApply的Function ,返回一个异常结束状态的CompletableFuture,内部的异常被CompletionException包装
↓
最终 future:就是第二个 thenApply返回的future,异常完成
当尝试
当尝试从future中获取结果时,不同的调用方法会得到不同的异常类型
java
future.get();
ExecutionException
└── IllegalStateException: 转换失败
future.join();
CompletionException
└── IllegalStateException: 转换失败
区别主要是:
- get() 抛出受检异常 ExecutionException
- join() 抛出运行时异常 CompletionException
正如我们上面提到的
三种常见异常处理方法
exceptionally:只处理异常
java
CompletableFuture<String> future =
CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("查询失败");
}).exceptionally(throwable -> {
System.out.println("发生异常:" + throwable.getMessage());
// 使用 HTML span 标签使"默认值"在渲染时显示为红色(实际代码中字符串内容不变)
return "默认值";
});
上游异常
↓
exceptionally 执行
↓
返回 "默认值"
↓
新的 future 正常完成
如果上游正常,exceptionally 不执行异常处理逻辑,正常结果直接传下去。
handle:正常和异常都会执行
java
CompletableFuture<String> future =
CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("失败");
})
.handle((result, error) -> error == null ? result : "fallback")
.thenApply(value -> value + "-processed");
System.out.println(future.join());
| 上游状态 | result | throwable |
|---|---|---|
| 正常完成 | 正常结果 | null |
| 异常完成 | 通常为 null | 异常对象 |
whenComplete:观察结果,不负责转换
java
CompletableFuture<String> future =
CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("查询失败");
}).whenComplete((result, throwable) -> {
if (throwable != null) {
System.out.println("记录异常");
}
});
取消在链中的传播
取消也是一种特殊的异常完成,但传播方向需要特别注意。
java
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> f1 =
CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "ddd";
});
CompletableFuture<Integer> f2 =
f1.thenApply(String::length);
CompletableFuture<String> f3 =
f2.thenApply(String::valueOf);
// Exception in thread "main" java.util.concurrent.CancellationException
f1.cancel(true);
f1.join();
// f2.join(); 抛出Exception in thread "main" java.util.concurrent.CompletionException: java.util.concurrent.CancellationException
}
Future.cancel(boolean)是否结束任务线程
| 操作 FutureTask 中任务是否继续 | CompletableFuture | 中任务是否继续 |
|---|---|---|
| get(timeout)超时 | 继续 | 继续 |
| 等待 get()的线程被中断 | 继续 | 继续 |
| cancel(false) | 已开始则通常继续,未开始则不执行 | 已开始通常继续,未开始也不保证阻止底层任务 |
| cancel(true) | 尝试中断工作线程,任务可能停止 | 不会中断工作线程,任务通常继续 |
| orTimeout(...) | 不适用 | 底层任务通常继续,只把 Future 标记为异常完成 |
| completeOnTimeout(...) | 不适用 | 底层任务通常继续,只给 Future 设置默认结果 |
可以看到只有cancel(true)有可能终止任务线程,而且需要任务线程响应中断,阻塞方法天然能通过中断事件响应中断
java
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(1);
Thread mainThread = Thread.currentThread();
Future<String> future = executorService.submit(() -> {
Thread.sleep(1000); // 1
return "ok";
});
// 中断执行get的线程
new Thread(() -> {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
mainThread.interrupt(); // 2
}).start();
try {
future.get(1000, TimeUnit.MILLISECONDS); // 3
} catch (InterruptedException e) {
System.out.println("InterruptedException mainThread before " + mainThread.isInterrupted());
// InterruptedException会清除中断标记,生产代码通常需要恢复
Thread.currentThread().interrupt();
System.out.println("InterruptedException mainThread after " + mainThread.isInterrupted());
} catch (ExecutionException e) {
System.out.println("ExecutionException " + e.getCause());
} catch (TimeoutException e) {
System.out.println("TimeoutException");
} catch (CancellationException e) {
System.out.println("CancellationException");
} finally {
executorService.shutdown();
}
}
执行步骤:
1、 先执行future.get(1000, TimeUnit.MILLISECONDS); 注释3
2、 再执行Thread.sleep(1000);注释1
3、 再执行mainThread.interrupt();注释2。此时mainThread会在future.get(1000, TimeUnit.MILLISECONDS);被唤醒,并抛出InterruptedException