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接口。
/**
* 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 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(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接口创建线程
/**
* 创建线程
*/
class MyRunnable implements Runnable {
@Override
public void run() {
log.info("正在执行任务...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 创建线程
*/
class MyCallable implements Callable {
@Override
public String call() {
log.info("正在执行任务...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "result";
}
}
- Callable + FutureTask
/**
* Callable + FutureTask
*
* @throws ExecutionException
* @throws InterruptedException
*/
@Test
public void testCallable() throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
FutureTask 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 futureTask2 = new FutureTask<>(myCallable2);
new Thread(futureTask2).start();
String result2 = futureTask2.get();
log.info("返回结果2:" + result2);
}
控制台打印:
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
/**
* Runnable + FutureTask
*
* @throws ExecutionException
* @throws InterruptedException
*/
@Test
public void testRunnable() throws ExecutionException, InterruptedException {
MyRunnable myRunnable = new MyRunnable();
FutureTask futureTask = new FutureTask<>(myRunnable, "custom result");
new Thread(futureTask).start();
String result = futureTask.get();
log.info("返回结果:" + result);
}
控制台打印:
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
/**
* Future + ExecutorService
*
* @throws ExecutionException
* @throws InterruptedException
*/
@Test
public void testFuture() throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
MyCallable myCallable = new MyCallable();
Future future = executorService.submit(myCallable);
String result = future.get();
log.info("返回结果:" + result);
MyRunnable myRunnable = new MyRunnable();
Future future2 = executorService.submit(myRunnable, "custom result2");
String result2 = future2.get();
log.info("返回结果2:" + result2);
executorService.shutdown();
}
控制台打印:
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
/**
* FutureTask + ExecutorService
*
* @throws ExecutionException
* @throws InterruptedException
*/
@Test
public void testFutureTask() throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
MyCallable myCallable = new MyCallable();
FutureTask futureTask = new FutureTask<>(myCallable);
executorService.submit(futureTask);
String result = futureTask.get();
log.info("返回结果:" + result);
MyRunnable myRunnable = new MyRunnable();
FutureTask futureTask2 = new FutureTask<>(myRunnable, "custom result2");
executorService.submit(futureTask2);
String result2 = futureTask2.get();
log.info("返回结果2:" + result2);
executorService.shutdown();
}
控制台打印:
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接口创建线程
/**
* 创建线程
*/
@RequiredArgsConstructor
class MyCallable implements Callable {
private final String taskName;
@Override
public String call() throws Exception {
log.info(taskName + "开始执行");
Thread.sleep(1000);
log.info(taskName + "结束执行");
return null;
}
}
- 取消任务1,mayInterruptIfRunning设置为true
/**
* 任务1开始执行,任务2未执行。
* 取消任务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();
}
控制台打印:
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
/**
* 任务1开始执行,任务2未执行。
* 取消任务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();
}
控制台打印:
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
/**
* 任务1开始执行,任务2未执行。
* 取消任务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();
}
控制台打印:
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
/**
* 任务1开始执行,任务2未执行。
* 取消任务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();
}
控制台打印:
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接口创建线程,并指定每个任务的执行时间。
/**
* 创建线程
*/
@RequiredArgsConstructor
class MyCallable implements Callable {
/**
* 任务执行时间
*/
private final long execTime;
@Override
public Long call() throws Exception {
Thread.sleep(execTime);
return execTime;
}
}
6.1 优点
- 并行执行多个任务
/**
* 优点:并行执行多个任务
*/
@Test
public void testConcurrent() {
ExecutorService executorService = Executors.newFixedThreadPool(3);
List> 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");
}
控制台打印:
23:27:49.398 [main] INFO com.c3stones.test.FutureTest - 总耗时:3006 ms
可以看出,3个任务并行执行,只需要耗时大约一个任务的时间。
6.2 缺点
- 使用get()方法获取结果会一直阻塞
/**
* 缺点:获取结果会一直阻塞
*/
@Test
public void testGet() throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
FutureTask futureTask = new FutureTask<>(new MyCallable(60_000));
executorService.submit(futureTask);
Long result = futureTask.get();
log.info("返回结果:" + result);
}
等待60秒,控制台打印:
00:03:48.954 [main] INFO com.c3stones.test.FutureTest - 返回结果:60000
查看get()方法源码:
/**
* @throws CancellationException {@inheritDoc}
*/
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
继续查看awaitDone方法源码:
/**
* 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异常
/**
* 缺点:超时抛出TimeoutException异常
*/
@Test
public void testGetTimeout() throws ExecutionException, InterruptedException, TimeoutException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
FutureTask futureTask = new FutureTask<>(new MyCallable(60_000));
executorService.submit(futureTask);
Long result = futureTask.get(1000, TimeUnit.MILLISECONDS);
log.info("返回结果:" + result);
}
等待1秒,控制台打印:
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空转,浪费资源
/**
* 缺点:轮询获取结果造成CPU空转,浪费资源
*/
@Test
public void testRoundGet() throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
FutureTask 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秒,控制台打印:
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