【并发编程】Java5 - Future,基本使用

1. 简介#

  在使用多线程开发中,不论是继承Thread类还是实现Runnable接口方式,都无法非常方便的获取异步任务执行的结果。在JDK1.5提供了和Runnable类似但多了返回值的Callable接口,通过Future接口实现类和Callable接口方式,可以非常灵活的进行多线程操作,例如:获取结果、指定超时时间获取结果、取消任务、判断是否取消、判断是否完成等。

2. Future接口API#

方法及参数 描述
boolean cancel(boolean mayInterruptIfRunning) 尝试取消此任务。如果任务正在执行,mayInterruptIfRunning为false则不会取消任务而是继续执行只到完成,为true则会取消任务;如果任务未开始执行,不论mayInterruptIfRunning为false还是true,均会取消任务
boolean isCancelled() 如果此任务在正常完成之前被取消,则返回true
boolean isDone() 如果此任务完成,则返回true 。正常终止、异常或取消此方法都将返回true
V get() 获取结果
V get(long timeout, TimeUnit unit) 获取结果,超时自动退出

3. FutureTask简介#

  FutureTask为Future接口的实现类,提供了Future的所有功能,并且可以将Callable和Runnable包装成FutureTask并交给Executor执行。

  通过FutureTask类图可以看出,RunnableFuture类实现了RunnableFuture接口,RunnableFuture接口又继承自Future接口和Runnable接口。

Copy
/** * Creates a {@code FutureTask} that will, upon running, execute the * given {@code Callable}. * * @param callable the callable task * @throws NullPointerException if the callable is null */ public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } /** * Creates a {@code FutureTask} that will, upon running, execute the * given {@code Runnable}, and arrange that {@code get} will return the * given result on successful completion. * * @param runnable the runnable task * @param result the result to return on successful completion. If * you don't need a particular result, consider using * constructions of the form: * {@code Future<?> f = new FutureTask<Void>(runnable, null)} * @throws NullPointerException if the runnable is null */ public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }

  FutureTask类提供了两个构造函数。当传入Callable接口的实现类时,可以通过Callable的run方法获取返回值;当存入Runnable接口时,还可以通过result参数自行指定返回值,当然如果并不需要返回值则result参数传null即可。

4. FutureTask应用#

  • 前期准备:分别实现Callable和Runable接口创建线程
Copy
/** * 创建线程 */ class MyRunnable implements Runnable { @Override public void run() { log.info("正在执行任务..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 创建线程 */ class MyCallable implements Callable<String> { @Override public String call() { log.info("正在执行任务..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "result"; } }
  • Callable + FutureTask
Copy
/** * Callable + FutureTask * * @throws ExecutionException * @throws InterruptedException */ @Test public void testCallable() throws ExecutionException, InterruptedException { MyCallable myCallable = new MyCallable(); FutureTask<String> futureTask = new FutureTask<>(myCallable); new Thread(futureTask).start(); String result = futureTask.get(); log.info("返回结果:" + result); // 使用Executors将Runnable转换为Callable // Executors.callable(Runnable task, T result) 可以自定义返回值 // Executors.callable(Runnable task) 返回值为null MyRunnable myRunnable = new MyRunnable(); Callable myCallable2 = Executors.callable(myRunnable, "custom result2"); FutureTask<String> futureTask2 = new FutureTask<>(myCallable2); new Thread(futureTask2).start(); String result2 = futureTask2.get(); log.info("返回结果2:" + result2); }

  控制台打印:

Copy
22:12:43.630 [Thread-1] INFO com.c3stones.test.FutureTaskTest - 正在执行任务... 22:12:44.637 [main] INFO com.c3stones.test.FutureTaskTest - 返回结果:result 22:12:44.641 [Thread-2] INFO com.c3stones.test.FutureTaskTest - 正在执行任务... 22:12:45.641 [main] INFO com.c3stones.test.FutureTaskTest - 返回结果2:custom result2
  • Runnable + FutureTask
Copy
/** * Runnable + FutureTask * * @throws ExecutionException * @throws InterruptedException */ @Test public void testRunnable() throws ExecutionException, InterruptedException { MyRunnable myRunnable = new MyRunnable(); FutureTask<String> futureTask = new FutureTask<>(myRunnable, "custom result"); new Thread(futureTask).start(); String result = futureTask.get(); log.info("返回结果:" + result); }

  控制台打印:

Copy
22:14:04.289 [Thread-1] INFO com.c3stones.test.FutureTaskTest - 正在执行任务... 22:14:05.296 [main] INFO com.c3stones.test.FutureTaskTest - 返回结果:custom result
  • Future + ExecutorService
Copy
/** * Future + ExecutorService * * @throws ExecutionException * @throws InterruptedException */ @Test public void testFuture() throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newSingleThreadExecutor(); MyCallable myCallable = new MyCallable(); Future<String> future = executorService.submit(myCallable); String result = future.get(); log.info("返回结果:" + result); MyRunnable myRunnable = new MyRunnable(); Future<String> future2 = executorService.submit(myRunnable, "custom result2"); String result2 = future2.get(); log.info("返回结果2:" + result2); executorService.shutdown(); }

  控制台打印:

Copy
22:14:42.179 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskTest - 正在执行任务... 22:14:43.185 [main] INFO com.c3stones.test.FutureTaskTest - 返回结果:result 22:14:43.187 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskTest - 正在执行任务... 22:14:44.187 [main] INFO com.c3stones.test.FutureTaskTest - 返回结果2:custom result2
  • FutureTask + ExecutorService
Copy
/** * FutureTask + ExecutorService * * @throws ExecutionException * @throws InterruptedException */ @Test public void testFutureTask() throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newSingleThreadExecutor(); MyCallable myCallable = new MyCallable(); FutureTask<String> futureTask = new FutureTask<>(myCallable); executorService.submit(futureTask); String result = futureTask.get(); log.info("返回结果:" + result); MyRunnable myRunnable = new MyRunnable(); FutureTask<String> futureTask2 = new FutureTask<>(myRunnable, "custom result2"); executorService.submit(futureTask2); String result2 = futureTask2.get(); log.info("返回结果2:" + result2); executorService.shutdown(); }

  控制台打印:

Copy
22:15:30.719 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskTest - 正在执行任务... 22:15:31.725 [main] INFO com.c3stones.test.FutureTaskTest - 返回结果:result 22:15:31.727 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskTest - 正在执行任务... 22:15:32.728 [main] INFO com.c3stones.test.FutureTaskTest - 返回结果2:custom result2

5. FutureTask任务取消#

  背景:创建两个任务,将两个任务依次添加到单一线程池中,此时任务1正在执行,任务2正在等待。

  • 前期准备:实现Callable接口创建线程
Copy
/** * 创建线程 */ @RequiredArgsConstructor class MyCallable implements Callable<String> { private final String taskName; @Override public String call() throws Exception { log.info(taskName + "开始执行"); Thread.sleep(1000); log.info(taskName + "结束执行"); return null; } }
  • 取消任务1,mayInterruptIfRunning设置为true
Copy
/** * 任务1开始执行,任务2未执行。<br/> * 取消任务1,mayInterruptIfRunning设置为true * * @throws ExecutionException * @throws InterruptedException */ @Test public void testCancalTask1ForTrue() throws ExecutionException, InterruptedException { FutureTask futureTask1 = new FutureTask(new MyCallable("任务1")); FutureTask futureTask2 = new FutureTask(new MyCallable("任务2")); ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.submit(futureTask1); executorService.submit(futureTask2); // 保证任务1开始执行 Thread.sleep(500); futureTask1.cancel(true); futureTask2.get(); executorService.shutdown(); }

  控制台打印:

Copy
23:06:01.946 [pool-2-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务1开始执行 23:06:02.446 [pool-2-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务2开始执行 23:06:03.446 [pool-2-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务2结束执行

  通过日志可以发现,任务1被取消成功。

  • 取消任务1,mayInterruptIfRunning设置为false
Copy
/** * 任务1开始执行,任务2未执行。<br/> * 取消任务1,mayInterruptIfRunning设置为false * * @throws ExecutionException * @throws InterruptedException */ @Test public void testCancalTask1ForFalse() throws ExecutionException, InterruptedException { FutureTask futureTask1 = new FutureTask(new MyCallable("任务1")); FutureTask futureTask2 = new FutureTask(new MyCallable("任务2")); ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.submit(futureTask1); executorService.submit(futureTask2); // 保证任务1开始执行 Thread.sleep(500); futureTask1.cancel(false); futureTask2.get(); executorService.shutdown(); }

  控制台打印:

Copy
23:05:59.926 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务1开始执行 23:06:00.932 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务1结束执行 23:06:00.932 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务2开始执行 23:06:01.933 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务2结束执行

  通过日志可以发现,任务1被取消失败。

  • 取消任务2,mayInterruptIfRunning设置为true
Copy
/** * 任务1开始执行,任务2未执行。<br/> * 取消任务2,mayInterruptIfRunning设置为true * * @throws ExecutionException * @throws InterruptedException */ @Test public void testCancalTask2ForTrue() throws ExecutionException, InterruptedException { FutureTask futureTask1 = new FutureTask(new MyCallable("任务1")); FutureTask futureTask2 = new FutureTask(new MyCallable("任务2")); ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.submit(futureTask1); executorService.submit(futureTask2); // 保证任务1开始执行 Thread.sleep(500); futureTask1.get(); futureTask2.cancel(true); executorService.shutdown(); }

  控制台打印:

Copy
23:10:09.233 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务1开始执行 23:10:10.238 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务1结束执行

  通过日志可以发现,任务2被取消成功。

  • 取消任务2,mayInterruptIfRunning设置为false
Copy
/** * 任务1开始执行,任务2未执行。<br/> * 取消任务2,mayInterruptIfRunning设置为false * * @throws ExecutionException * @throws InterruptedException */ @Test public void testCancalTask2ForFalse() throws ExecutionException, InterruptedException { FutureTask futureTask1 = new FutureTask(new MyCallable("任务1")); FutureTask futureTask2 = new FutureTask(new MyCallable("任务2")); ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.submit(futureTask1); executorService.submit(futureTask2); // 保证任务1开始执行 Thread.sleep(500); futureTask1.get(); futureTask2.cancel(false); executorService.shutdown(); }

  控制台打印:

Copy
23:11:03.170 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务1开始执行 23:11:04.177 [pool-1-thread-1] INFO com.c3stones.test.FutureTaskCancelTest - 任务1结束执行

  通过日志可以发现,任务2被取消成功。

  结论:

mayInterruptIfRunning 正在执行的任务 等待中的任务
true 成功 成功
false 失败 成功

6. FutureTask优缺点#

  前期准备:实现Callable接口创建线程,并指定每个任务的执行时间。

Copy
/** * 创建线程 */ @RequiredArgsConstructor class MyCallable implements Callable<Long> { /** * 任务执行时间 */ private final long execTime; @Override public Long call() throws Exception { Thread.sleep(execTime); return execTime; } }
6.1 优点
  • 并行执行多个任务
Copy
/** * 优点:并行执行多个任务 */ @Test public void testConcurrent() { ExecutorService executorService = Executors.newFixedThreadPool(3); List<Future<Long>> futureList = new ArrayList<>(3); // 记录开始时间 StopWatch stopWatch = new StopWatch(); stopWatch.start(); // 创建3个任务 futureList.add(executorService.submit(new MyCallable(3000))); futureList.add(executorService.submit(new MyCallable(3000))); futureList.add(executorService.submit(new MyCallable(3000))); // 阻塞获取结果 futureList.forEach(f -> { try { f.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }); stopWatch.stop(); log.info("总耗时:" + stopWatch.getTotalTimeMillis() + " ms"); }

  控制台打印:

Copy
23:27:49.398 [main] INFO com.c3stones.test.FutureTest - 总耗时:3006 ms

  可以看出,3个任务并行执行,只需要耗时大约一个任务的时间。

6.2 缺点
  • 使用get()方法获取结果会一直阻塞
Copy
/** * 缺点:获取结果会一直阻塞 */ @Test public void testGet() throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(1); FutureTask<Long> futureTask = new FutureTask<>(new MyCallable(60_000)); executorService.submit(futureTask); Long result = futureTask.get(); log.info("返回结果:" + result); }

  等待60秒,控制台打印:

Copy
00:03:48.954 [main] INFO com.c3stones.test.FutureTest - 返回结果:60000

  查看get()方法源码:

Copy
/** * @throws CancellationException {@inheritDoc} */ public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s); }

  继续查看awaitDone方法源码:

Copy
/** * Awaits completion or aborts on interrupt or timeout. * * @param timed true if use timed waits * @param nanos time to wait, if timed * @return state upon completion */ private int awaitDone(boolean timed, long nanos) throws InterruptedException { final long deadline = timed ? System.nanoTime() + nanos : 0L; WaitNode q = null; boolean queued = false; for (;;) { if (Thread.interrupted()) { removeWaiter(q); throw new InterruptedException(); } int s = state; if (s > COMPLETING) { if (q != null) q.thread = null; return s; } else if (s == COMPLETING) // cannot time out yet Thread.yield(); else if (q == null) q = new WaitNode(); else if (!queued) queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q); else if (timed) { nanos = deadline - System.nanoTime(); if (nanos <= 0L) { removeWaiter(q); return state; } LockSupport.parkNanos(this, nanos); } else LockSupport.park(this); } }

  通过DEBUG跟踪代码可以发现,最终会调用LockSupport.park(Object blocker)方法。用LockSupport.park()和Thread.sleep()类似,都会阻塞当前线程,且都不会释放当前线程占有的锁资源,区别在于LockSupport.park()方法会被其他线程调用LockSupport.unpark()方法唤醒,或调用Thread.interrupt()方法唤醒。
  当我们在代码中使用get()方法等待结果时,并不能处理其他的任务,相当于同步执行。

  • 使用get(long timeout, TimeUnit unit)方法超时会抛出TimeoutException异常
Copy
/** * 缺点:超时抛出TimeoutException异常 */ @Test public void testGetTimeout() throws ExecutionException, InterruptedException, TimeoutException { ExecutorService executorService = Executors.newFixedThreadPool(1); FutureTask<Long> futureTask = new FutureTask<>(new MyCallable(60_000)); executorService.submit(futureTask); Long result = futureTask.get(1000, TimeUnit.MILLISECONDS); log.info("返回结果:" + result); }

  等待1秒,控制台打印:

Copy
java.util.concurrent.TimeoutException at java.util.concurrent.FutureTask.get(FutureTask.java:205) at com.c3stones.test.FutureTest.testGetTimeout(FutureTest.java:96) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725) at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84) at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115) at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104) at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.util.ArrayList.forEach(ArrayList.java:1249) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.util.ArrayList.forEach(ArrayList.java:1249) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67) at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86) at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86) at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53) at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
  • 轮询获取结果造成CPU空转,浪费资源
Copy
/** * 缺点:轮询获取结果造成CPU空转,浪费资源 */ @Test public void testRoundGet() throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(1); FutureTask<Long> futureTask = new FutureTask<>(new MyCallable(60_000)); executorService.submit(futureTask); // 可以做其他事 // ... // 最后轮询判断任务是否结束,结束后获取结果 Long result; while (true) { if (futureTask.isDone()) { result = futureTask.get(); break; } } log.info("返回结果:" + result); }

  等待60秒,控制台打印:

Copy
00:11:19.338 [main] INFO com.c3stones.test.FutureTest - 返回结果:60000

  综上所述问题,Guava工具包提供了ListenableFuture,随后JDK1.8也推出CompletableFuture。
  详情请浏览:
  【并发编程】Java5 - CompletionService,将异步执行与获取结果分离
  【并发编程】Guava - ListenableFuture,避免Future获取阻塞问题,增加回调
  【并发编程】Java8 - CompletableFuture,增强版Future

7. 项目地址#

  thread-demo

posted @   C3Stones  阅读(81)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
历史上的今天:
2017-03-28 蓝桥杯 历届试题 PREV-1 核桃的数量
点击右上角即可分享
微信分享提示
CONTENTS