future java 原理_Java8异步编程之CompletableFuture源码解读

【51CTO.com原创稿件】

一、引言

一说到异步任务,很多人上来咔咔新建个线程池。为了防止线程数量肆虐,一般还会考虑使用单例模式创建线程池,具体使用方法大都如下面的代码所示:

@Test

publicvoiddemo1() throwsExecutionException, InterruptedException {

ExecutorServiceexecutorService=Executors.newFixedThreadPool(5);

Futurefuture1=executorService.submit(newCallable() {

@Override

publicObjectcall() throwsException {

returnThread.currentThread().getName();}

});

System.out.println(future1.get());

executorService.execute(newRunnable() {

@Overridepublicvoidrun() {

System.out.println(Thread.currentThread().getName());

}

});

}

经常使用 JavaScript 的同学相信对于异步回调的用法相当熟悉了,毕竟 JavaScript 拥有“回调地狱”的美誉。

我们大 Java 又开启了新一轮模仿之旅。

java.util.concurrent 包新增了 CompletableFuture 类可以实现类似 JavaScript 的连续回调。

二、两种基本用法

先来看下 CompletableFuture 的两种基本⽤法,代码如下:

@Test

publicvoid index1() throws ExecutionException, InterruptedException {

CompletableFuture completableFuture1 = CompletableFuture.supplyAsync(() -> Thread.currentThread().getName());

CompletableFuture completableFuture2 = CompletableFuture.runAsync(() -> Thread.currentThread().getName());

System.out.println(completableFuture1.get()); System.out.println(completableFuture2.get());

}

打印输出:

ForkJoinPool.commonPool-worker-1

null

初看代码,第一反应是代码简洁。直接调用 CompletableFuture 类的静态方法,提交任务方法就完事了。但是,随之而来的疑问就是,异步任务执行的背后是一套什么逻辑呢?是一对一使用newThread()还是依赖线程池去执行的呢。

三、探索线程池原理

翻阅 CompletableFuture 类的源码,我们找到答案。关键代码如下:

private static final booleanuseCommonPool=

(ForkJoinPool.getCommonPoolParallelism() >1);

/**

* Default executor -- ForkJoinPool.commonPool() unless it cannot

* support parallelism.

*/

private static final Executor asyncPool=useCommonPool?

ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

可以看到 CompletableFuture 类默认使⽤的是 ForkJoinPool.commonPool() ⽅法返回的线程池。当 然啦,前提是 ForkJoinPool 线程池的数量⼤于 1 。否则,则使⽤ CompletableFuture 类⾃定义的 ThreadPerTaskExecutor 线程池。 ThreadPerTaskExecutor 线程池的实现逻辑⾮常简单,⼀⾏代码简单实现了 Executor 接⼝,内部执⾏ 逻辑是⼀条任务对应⼀条线程。代码如下:

/** Fallback if ForkJoinPool.commonPool() cannot support parallelism */

staticfinal class ThreadPerTaskExecutor implements Executor {

publicvoidexecute(Runnable r) { new Thread(r).start(); }

}

四、两种异步接⼝

之前我们使⽤线程池执⾏异步任务时,当不需要任务执⾏完毕后返回结果的,我们都是实现 Runnable 接⼝。⽽当需要实现返回值时,我们使⽤的则是 Callable 接⼝。 同理,使⽤ CompletableFuture 类的静态⽅法执⾏异步任务时,不需要返回结果的也是实现 Runnable 接⼝。⽽当需要实现返回值时,我们使⽤的则是 Supplier 接⼝。其实,Callable 接⼝和 Supplier 接⼝ 并没有什么区别。 接下来,我们来分析⼀下 CompletableFuture 是如何实现异步任务执⾏的。

runAsync

CompletableFuture 执⾏⽆返回值任务的是 runAsync() ⽅法。该⽅法的关键执⾏代码如下:

staticCompletableFuture asyncRunStage(Executor e, Runnable f) {

if (f == null) throw new NullPointerException();

CompletableFuture d = new CompletableFuture();

e.execute(new AsyncRun(d, f));

returnd;

}

可以看到,该⽅法将 Runnable 实例作为参数封装⾄ AsyncRun 类。实际上, AsyncRun 类是对 Runnable 接⼝的进⼀步封装。实际上,AsyncRun 类也是实现了 Runnable 接⼝。观察下⽅ AsyncRun 类的源码,可以看到 AsyncRun 类的 run() ⽅法中调⽤了 Runnable 参数的 run() ⽅法。

publicvoid run() {

CompletableFuture d; Runnable f;

if ((d = dep) != null&& (f = fn) !=null) {

dep = null; fn =null;

if (d.result == null) {

try {

f.run();

d.completeNull();

} catch (Throwable ex) {

d.completeThrowable(ex);

}

}

d.postComplete();

}

}

当提交的任务执⾏完毕后,即 f.run() ⽅法执⾏完毕。调⽤ d.completeNull() ⽅法设置任务执⾏结 果为空。代码如下:

/** The encodingofthenullvalue. */

staticfinal AltResult NIL = new AltResult(null);

/** Completes withthenullvalue, unless already completed. */

final boolean completeNull() {

returnUNSAFE.compareAndSwapObject(this, RESULT,n

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值