Java中Future触发TimeoutException时如何获取任务返回值?
如何在Future超时后获取任务返回值
首先,咱们先理清你代码里的逻辑问题:当你调用future.get(2, TimeUnit.SECONDS)超时抛出TimeoutException时,你的sayHello任务还在执行(它要睡3秒,2秒时还没跑完)。这时候你调用future.cancel(true)会中断任务线程,Thread.sleep(3000)会抛出InterruptedException,触发catch块返回"hello"——但问题在于,Future被标记为已取消状态后,再调用future.get()会直接抛出CancellationException,根本拿不到这个"hello"。
那要怎么拿到超时中断后的返回值呢?这里给你两个实用的解决方案:
方案一:用线程安全容器保存任务结果
我们可以在提交任务时,把返回值同步到一个线程安全的容器里,比如AtomicReference,这样不管Future状态如何,都能从容器里取到结果。修改后的代码如下:
import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.junit.Test; public class FutureTest { public static String sayHello() { try { Thread.sleep(3000); return "world"; } catch (Exception e) { // 线程被中断时会走到这里,返回"hello" return "hello"; } } @Test public void testTimeoutAndGetResult() throws Exception { ExecutorService executorService = new ThreadPoolExecutor(0, Runtime.getRuntime().availableProcessors() * 2, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10), new BasicThreadFactory.Builder().namingPattern("executor-pool-%d").daemon(true).build()); // 用AtomicReference保存任务结果,线程安全 AtomicReference<String> resultHolder = new AtomicReference<>(); Future<String> future = executorService.submit(() -> { String result = sayHello(); resultHolder.set(result); // 把结果存入容器 return result; }); try { String result = future.get(2, TimeUnit.SECONDS); System.out.println("future result:" + result); } catch (TimeoutException e) { System.out.println("timeout exception"); future.cancel(true); // 中断任务,触发返回"hello" // 等待任务执行完毕(cancel后任务还是会跑完return逻辑) while (!future.isDone()) { Thread.sleep(100); } // 从容器里拿最终结果 String finalResult = resultHolder.get(); System.out.println("result after timeout: " + finalResult); // 这里会输出"hello" } finally { executorService.shutdown(); } } }
方案二:不立即取消任务,等待任务自然完成
如果你不需要中断任务,只是想在超时后继续等待任务完成拿结果,那可以去掉future.cancel(true)的调用,直接在超时后再次调用future.get():
@Test public void testTimeoutWaitComplete() throws Exception { ExecutorService executorService = new ThreadPoolExecutor(0, Runtime.getRuntime().availableProcessors() * 2, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10), new BasicThreadFactory.Builder().namingPattern("executor-pool-%d").daemon(true).build()); Future<String> future = executorService.submit(FutureTest::sayHello); try { String result = future.get(2, TimeUnit.SECONDS); System.out.println("future result:" + result); } catch (TimeoutException e) { System.out.println("timeout exception"); // 不取消任务,继续等待它完成 String delayedResult = future.get(); System.out.println("result after wait: " + delayedResult); // 这里会输出"world",因为任务正常跑完了 } finally { executorService.shutdown(); } }
关键知识点
Future.cancel(true)只会中断任务线程,但任务本身会继续执行到return语句(除非你在任务里主动检查中断状态提前退出)。- 一旦调用
cancel(true),Future会被标记为已取消,后续调用future.get()会抛出CancellationException,无法通过Future直接获取结果。 - 用外部线程安全容器存储结果,是绕过Future取消状态限制的最直接方式。
内容的提问来源于stack exchange,提问作者LiQIang.Feng
相关产品推荐
相关产品推荐

