Future、JDK1.8后的Completefuture以及Guava提供的Future简单例子
一、前言
Java 1.5开始,提供Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。
当我们需要一个函数方法时,如果这个函数执行的很慢,啊么我们就要进行等待。但有时候,我们并不急着要结果。
因此,我们可以让被调用者立即返回,让他再后台慢慢处理这个请求。对于调用者来说,则可以先处理一些其他任务,在正真需要数据的场合再去尝试获取需要的数据。

二、Future
一般地,是配合ExecutorService来使用的
如demo:
/**
* 简单调用future+callable
*/
public static void demo1() {
ExecutorService executorService = Executors.newCachedThreadPool();
Future<Integer> result = executorService.submit(
new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return new Random().nextInt();
}
}
);
executorService.shutdown();
try {
System.out.println("result:" + result.get());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 简单调用futureTask+callable2
*/
public static void demo2() {
FutureTask<Integer> task = new FutureTask<>(
new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return new Random().nextInt();
}
}
);
new Thread(task).start();
try {
System.out.println("result:" + task.get());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 简单调用futureTask+callable3
*/
public static void demo3() {
ExecutorService executorService = Executors.newCachedThreadPool();
FutureTask<Integer> task = new FutureTask<Integer>(
() -> {
return new Random().nextInt();
}
);
executorService.submit(task);
try {
System.out.println("result:" + task.get());
} catch (Exception e) {
e.printStackTrace();
}
executorService.shutdown();
}
或者以下demo
// 建立线程池 demo1
final static ExecutorService service = Executors.newCachedThreadPool();
public static void demo1() throws InterruptedException, ExecutionException {
Long t1 = System.currentTimeMillis();
// 任務1
Future<Boolean> booleanTask = service.submit(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return true;
}
});
while (true) {
if (booleanTask.isDone() && !booleanTask.isCancelled()) {
//模擬耗時
Thread.sleep(500);
Boolean result = booleanTask.get();
System.err.println("BooleanTask: " + result);
break;
}
}
// 任務2
Future<String> stringTask = service.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "Hello World";
}
});
while (true) {
if (stringTask.isDone() && !stringTask.isCancelled()) {
String result = stringTask.get();
System.err.println("StringTask: " + result);
break;
}
}
// 任務3
Future<Integer> integerTask = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return new Random().nextInt(100);
}
});
while (true) {
if (integerTask.isDone() && !integerTask.isCancelled()) {
Integer result = integerTask.get();
System.err.println("IntegerTask: " + result);
break;
}
}
// 執行時間
System.err.println("time: " + (System.currentTimeMillis() - t1));
}
我们查看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;
}
综上所述,Future就是对于具体的Runable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。
必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果
我们可以通过轮询isDone方法判断其是否完成,但该方法有较多局限。
三、Future的局限性以及同步、异步、阻塞、非阻塞相关
Future很难直接表述多个Future结果之间的依赖性,开发中,我们经常需要达成以下目的:
1、将两个异步计算合并为一个(两个计算之间相互独立,同时第二个又依赖于第一个的结果)
2、等待Future集合中的所有任务都完成
3、仅等待Future集合中最快的任务完成,并返回它的结果
同步和异步:针对应用程序来,关注的是程序中间的协作关系;阻塞与非阻塞:更关注的是单个进程的执行状态。
同步:执行一个操作之后,等待结果,然后才继续执行后续的操作。
异步:执行一个操作后,可以去执行其他的操作,然后等待通知再回来执行刚才没执行完的操作。
阻塞:进程给CPU传达一个任务之后,一直等待CPU处理完成,然后才执行后面的操作。
非阻塞:进程给CPU传达任务后,继续处理后续的操作,隔断时间再来询问之前的操作是否完成。这样的过程其实也叫轮询
四、Future的两个增强API
Java8 CompletableFuture
其实现了CompletionStage和Future接口,因此你可以像Future那样使用它。
四个静态方法用来为一段异步执行的代码创建CompletableFuture对象,方法的参数类型都是函数式接口,所以可以使用lambda表达式实现异步任务
runAsync方法:它以Runnabel函数式接口类型为参数,所以CompletableFuture的计算结果为空。
supplyAsync方法以Supplier函数式接口类型为参数,CompletableFuture的计算结果类型为U。
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
其他API可以自行查阅资料,在此贴出一些简单的介绍demo
1、将两个异步计算合并为一个(这两个异步计算之间相互独立,同时第二个又依赖于第一个的结果)
/**
* CompletableFuture的hello world join方法官方解释[Returns the result value when complete]
*/
public static void demo4() {
String result = CompletableFuture.supplyAsync(
() -> {
System.out.println("build hello");
return "hello";
}
).thenApplyAsync(
r -> {
System.out.println("build world");
return r + " world";
}
).join();
System.out.println(result);
}
2、同上,但是不返回最后结果,仅做逻辑处理
/**
* CompletableFuture,最后使用thenAccept消费结果,并不产生返回值
*/
public static void demo5() {
CompletableFuture.supplyAsync(() -> "hello").thenApplyAsync(r -> r + " world").thenAccept(System.out::println);
}
3、多任务结果合并处理后返回
/**
* thenCombine 结合两个CompletionStage的结果,返回。 如果是消费结果不反回用thenAcceptBoth
*/
public static void demo6() {
String result = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
System.out.println("build hello");
} catch (Exception e) {
e.printStackTrace();
}
return "hello";
}).thenCombine(CompletableFuture.supplyAsync(() -> {
try {
System.out.println("build world");
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
return " world";
}),(s1,s2) -> s1 + s2).join();
System.out.println(result);
}
4、择优
/**
* applyToEither可以在两个CompletionStage中选去计算的快的结果返用于下一步的处理
*/
public static void demo7() {
String result = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
return "Hi boy";
}).applyToEither(CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(300);
} catch (Exception e) {
e.printStackTrace();
}
return "Hi girl";
}),s -> s).join();
System.out.println(result);
}
5、补偿机制
/**
* 运行时出现异常,可以通过exceptionally进行补偿
*/
public static void demo8() {
String result = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
int a = 10;
a = a / 0;
a = a++;
return "hi boy";
}).exceptionally(e -> {
System.out.println(e.getMessage());
return "hello world";
}).join();
System.out.println(result);
}
Google Guava ListenableFuture
其特点:
1、减少主函数的等待时间,使得多任务能够异步非阻塞执行
2、ListenableFuture顾名思义就是可以监听的Future,它是对java原生Future的扩展增强。
3、我们知道Future表示一个异步计算任务,当任务完成时可以得到计算结果。
如果我们希望一旦计算完成就拿到结果展示给用户或者做另外的计算,就必须使用另一个线程不断的查询计算状态。这样做,代码复杂,而且效率低下。
使用ListenableFuture Guava帮我们检测Future是否完成了,如果完成就自动调用回调函数,这样可以减少并发程序的复杂度。
4、ListenableFuture是一个接口,它从jdk的Future接口继承,添加了void addListener(Runnable listener, Executor executor)方法。
demo:
public static void demo2() throws Exception {
Long t1 = System.currentTimeMillis();
// 任务1
ListenableFuture<Boolean> booleanTask = service.submit(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return true;
}
});
Futures.addCallback(booleanTask, new FutureCallback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
System.err.println("BooleanTask: " + result);
}
@Override
public void onFailure(Throwable t) {
}
},service);
// 任务2
ListenableFuture<String> stringTask = service.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "Hello World";
}
});
Futures.addCallback(stringTask, new FutureCallback<String>() {
@Override
public void onSuccess(String result) {
System.err.println("StringTask: " + result);
}
@Override
public void onFailure(Throwable t) {
}
},service);
// 任务3
ListenableFuture<Integer> integerTask = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return new Random().nextInt(100);
}
});
Futures.addCallback(integerTask, new FutureCallback<Integer>() {
@Override
public void onSuccess(Integer result) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.err.println("IntegerTask: " + result);
}
@Override
public void onFailure(Throwable t) {
}
},service);
// 执行时间
System.err.println("time: " + (System.currentTimeMillis() - t1));
}
1万+

被折叠的 条评论
为什么被折叠?



