JDK 实现
public class FutureTest { public static void main(String[] args) throws Exception { ExecutorService es = Executors.newFixedThreadPool(10); Future<Integer> f = es.submit(() ->{ Thread.sleep(5000); // 结果 return 100; }); Integer result = f.get(); System.out.println(result); // 也可以轮询等结束 // while (f.isDone()) { // System.out.println(result); // } } }
虽然这些方法提供了异步执行任务的能力,但是对于结果的获取却还是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。
阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的CPU资源,而且也不能及时的得到计算结果。
Java的一些框架,
Netty,自己扩展了Java的 Future 接口,提供了 addListener 等多个扩展方法。
Google的guava也提供了通用的扩展Future:ListenableFuture 、 SettableFuture 以及辅助类 Futures 等,方便异步编程。
Java 在JDK1.8 这个版本中增加了一个能力更强的Future类:CompletableFuture 。它提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果。下面来看看这几种方式。
Netty-Future
引入Maven依赖:
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.50.Final</version> </dependency>
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.3.7</version> </dependency>
package com.vipsoft; import cn.hutool.core.date.DateUtil; import io.netty.util.concurrent.DefaultEventExecutorGroup; import io.netty.util.concurrent.EventExecutorGroup; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; public class FutureTest { public static void main(String[] args) throws InterruptedException { EventExecutorGroup group = new DefaultEventExecutorGroup(4); System.out.println("开始:" + DateUtil.now()); Future<Integer> f = group.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { System.out.println("开始耗时计算:" + DateUtil.now()); Thread.sleep(5000); System.out.println("结束耗时计算:" + DateUtil.now()); int a = 0; int b = 1; int c = b / a; return 100; } }); //通过监听,待线程结束后,自动触发,避免了主线程 的阻塞和等待 f.addListener(new FutureListener<Object>() { @Override public void operationComplete(Future<Object> objectFuture) throws Exception { System.out.println("计算结果:" + objectFuture.get()); } }); System.out.println("结束:" + DateUtil.now()); // 不让守护线程退出 new CountDownLatch(1).await(); } }
在Listener添加成功之后,会立即检查状态,如果任务已经完成立刻进行回调,通过监听,待线程结束后,自动触发,避免了主线程 的阻塞和等待
Guava-Future
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>29.0-jre</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.3.7</version> </dependency>
package com.vipsoft; import cn.hutool.core.date.DateUtil; import com.google.common.util.concurrent.*; import javax.annotation.Nullable; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class FutureTest { public static void main(String[] args) throws InterruptedException { System.out.println("开始:" + DateUtil.now()); ExecutorService executorService = Executors.newFixedThreadPool(10); ListeningExecutorService service = MoreExecutors.listeningDecorator(executorService); ListenableFuture<Integer> future = service.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { System.out.println("开始耗时计算:" + DateUtil.now()); Thread.sleep(5000); System.out.println("结束耗时计算:" + DateUtil.now()); return 100; } }); //增加回调函数,一般用于不在乎执行结果的地方 future.addListener(new Runnable() { @Override public void run() { System.out.println("调用成功--不关心结果"); } }, executorService); //通过addCallback 获得结果 Futures.addCallback(future, new FutureCallback<Integer>() { @Override public void onSuccess(@Nullable Integer result) { System.out.println("成功,计算结果:" + result); } @Override public void onFailure(Throwable t) { System.out.println("失败"); } }, executorService); System.out.println("结束:" + DateUtil.now()); new CountDownLatch(1).await(); } }
CompletableFuture
package com.vipsoft; import cn.hutool.core.date.DateUtil; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; public class FutureTest { public static void main(String[] args) throws InterruptedException { System.out.println("开始:" + DateUtil.now()); CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> { System.out.println("开始耗时计算:" + DateUtil.now()); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("结束耗时计算:" + DateUtil.now()); return 100; }); //使用 thenCompose 或者 thenComposeAsync 等方法可以实现回调的回调,且写出来的方法易于维护。 completableFuture = completableFuture.thenCompose(i -> { return CompletableFuture.supplyAsync(() -> { System.out.println("在回调的回调中执行耗时操作..."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } return i + 200; }); }); completableFuture.whenComplete((result, e) -> { System.out.println("回调结果:" + result); }); System.out.println("结束:" + DateUtil.now()); new CountDownLatch(1).await(); } }
JDK1.8 已经提供了一种更为高级的回调方式:CompletableFuture,不需要引入任何第三方的依赖,为Future模式增加回调功能就不需要阻塞等待结果的返回并且不需要消耗无谓的CPU资源去轮询处理状态,JDK8之前使用Netty或者Guava提供的工具类,JDK8之后则可以使用自带的 CompletableFuture 类。Future 有两种模式:将来式和回调式。而回调式会出现回调地狱的问题,由此衍生出了 Promise 模式来解决这个问题。这才是 Future 模式和 Promise 模式的相关性。