二、Future
1. 描述
- Future 表示异步计算的结果。
- 提供了检查计算是否完成、等待其完成以及检索计算结果的方法。
- 结果只能在计算完成后使用方法 get 检索,必要时阻塞,直到它准备好。
- 取消是通过取消方法执行的。提供了额外的方法来确定任务是正常完成还是被取消。一旦计算完成,就不能取消计算。
- 如果想使用 Future 来取消可取消性但不提供可用的结果,您可以声明 Future<?> 形式的类型并返回 null 作为底层任务的结果。
Future源码:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
2. 使用
- 如果主线程需要执行一个很耗时的任务,可以通过Future把这个任务放到异步线程中执行
- 主线程继续处理其他任务或者先行结束,可以通过get获取计算结果
- 一句话总结:Future接口可以为主线程开一个异步线程,专门为主线程执行耗时任务
Future接口的实现类需要满足3个特点:多线程、有返回值、执行异步任务
FutureTask实现类可以满足以上3个特点
- FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况。
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建任务对象
FutureTask<Integer> futureTask = new FutureTask(() -> {
log.debug("callable执行任务");
Thread.sleep(1000);
return 100;
});
// 参数1 是任务对象; 参数2 是线程名
Thread t1 = new Thread(futureTask, "t1");
t1.start();
// 主线程阻塞,同步等待 futureTask 执行完毕的结果
Integer result = futureTask.get();
// 1秒后获取到结果
log.debug("获取FutureTask结果:{}", result);
}
22:36:52.884 c.FutureTaskThread [t1] - callable执行任务
22:36:53.899 c.FutureTaskThread [main] - 获取FutureTask结果:100
Process finished with exit code 0
Callable源码
// Callable是函数式接口,有返回结果,能抛出异常
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
FutureTask源码
// 1.FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况,get方法可以获取返回结果
// 2.FutureTask类是 Runnable接口 的扩展,FutureTask 既有Runnable中的run方法,又有Future中的get方法
public class FutureTask<V> implements RunnableFuture<V> {
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
}
// 接口可以多继承
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
- 优点:FutureTask配合线程池执行异步任务,可以提升执行效率
@Slf4j(topic = "c.TestFutureTask")
public class TestFutureTask {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(3);
long start = System.currentTimeMillis();
FutureTask<Integer> task1 = new FutureTask<>(() -> {
Thread.sleep(1000);
return 1;
});
pool.submit(task1);
FutureTask<Integer> task2 = new FutureTask<>(() -> {
Thread.sleep(2000);
return 2;
});
pool.submit(task2);
FutureTask<Integer> task3 = new FutureTask<>(() -> {
Thread.sleep(3000);
return 3;
});
pool.submit(task3);
log.debug("{}", task1.get());
log.debug("{}", task2.get());
log.debug("{}", task3.get());
long end = System.currentTimeMillis();
log.debug("总耗时:{}毫秒", end - start);
pool.shutdown();
}
}
16:08:38.938 c.TestFutureTask [main] - 1
16:08:39.931 c.TestFutureTask [main] - 2
16:08:40.934 c.TestFutureTask [main] - 3
16:08:40.934 c.TestFutureTask [main] - 总耗时:3065毫秒
- isDone()轮询获取异步任务结果
@Slf4j(topic = "c.TestFutureTask")
public class TestFutureTask {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(1);
long start = System.currentTimeMillis();
FutureTask<Integer> task1 = new FutureTask<>(() -> {
Thread.sleep(3000);
return 1;
});
pool.submit(task1);
while (true) {
if (task1.isDone()) {
log.debug("{}", task1.get());
break;
} else {
Thread.sleep(500);
log.debug("正在处理中,请等到。。。。");
}
}
long end = System.currentTimeMillis();
log.debug("总耗时:{}毫秒", end - start);
pool.shutdown();
}
}
16:24:00.121 c.TestFutureTask [main] - 正在处理中,请等到。。。。
16:24:00.632 c.TestFutureTask [main] - 正在处理中,请等到。。。。
16:24:01.139 c.TestFutureTask [main] - 正在处理中,请等到。。。。
16:24:01.644 c.TestFutureTask [main] - 正在处理中,请等到。。。。
16:24:02.150 c.TestFutureTask [main] - 正在处理中,请等到。。。。
16:24:02.666 c.TestFutureTask [main] - 正在处理中,请等到。。。。
16:24:02.666 c.TestFutureTask [main] - 1
16:24:02.667 c.TestFutureTask [main] - 总耗时:3101毫秒
FutureTask缺点:
- get()阻塞,调用get方法计算结果,如果计算没有完成容易导致程序阻塞
- isDone()轮询会耗费无畏的CPU资源,并且也不能及时得到计算结果