博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JDK1.8
阅读量:5888 次
发布时间:2019-06-19

本文共 7156 字,大约阅读时间需要 23 分钟。

hot3.png

 在JDK1.5已经提供了Future和Callable的实现,可以用于阻塞式获取结果,如果想要异步获取结果,通常都会以轮询的方式去获取结果,如下:

//定义一个异步任务Future
future = executor.submit(()->{ Thread.sleep(2000); return "hello world";});//轮询获取结果while (true){ if(future.isDone()) { System.out.println(future.get()); break; } }

    从上面的形式看来轮询的方式会耗费无谓的CPU资源,而且也不能及时地得到计算结果.所以要实现真正的异步,上述这样是完全不够的,在Netty中,我们随处可见异步编程

ChannelFuture f = serverBootstrap.bind(port).sync();f.addListener(new GenericFutureListener
>() { @Override public void operationComplete(Future
future) throws Exception { System.out.println("complete"); } });

    而JDK1.8中的CompletableFuture就为我们提供了异步函数式编程,CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。

1. 创建CompletableFuture对象

    CompletableFuture提供了四个静态方法用来创建CompletableFuture对象:

public static CompletableFuture
runAsync(Runnable runnable)public static CompletableFuture
runAsync(Runnable runnable, Executor executor)public static
CompletableFuture supplyAsync(Supplier supplier)public static CompletableFuture supplyAsync(Supplier supplier, Executor executor)

    Asynsc表示异步,而supplyAsyncrunAsync不同在与前者异步返回一个结果,后者是void.第二个函数第二个参数表示是用我们自己创建的线程池,否则采用默认的ForkJoinPool.commonPool()作为它的线程池.其中Supplier是一个函数式接口,代表是一个生成者的意思,传入0个参数,返回一个结果.(更详细的可以看我另一篇文章)

CompletableFuture
future = CompletableFuture.supplyAsync(()->{ return "hello world"; });System.out.println(future.get()); //阻塞的获取结果 ''helllo world"

2. 主动计算

    以下4个方法用于获取结果

//同步获取结果public T    get()public T    get(long timeout, TimeUnit unit)public T    getNow(T valueIfAbsent)public T    join()

getNow有点特殊,如果结果已经计算完则返回结果或者抛出异常,否则返回给定的valueIfAbsent值。join()get()区别在于join()返回计算的结果或者抛出一个unchecked异常(CompletionException),而get()返回一个具体的异常.

  • 主动触发计算.
public boolean complete(T  value)public boolean completeExceptionally(Throwable ex)

上面方法表示当调用CompletableFuture.get()被阻塞的时候,那么这个方法就是结束阻塞,并且get()获取设置的value.

public static CompletableFuture
compute() { final CompletableFuture
future = new CompletableFuture<>(); return future; } public static void main(String[] args) throws Exception { final CompletableFuture
f = compute(); class Client extends Thread { CompletableFuture
f; Client(String threadName, CompletableFuture
f) { super(threadName); this.f = f; } @Override public void run() { try { System.out.println(this.getName() + ": " + f.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } new Client("Client1", f).start(); new Client("Client2", f).start(); System.out.println("waiting"); //设置Future.get()获取到的值 f.complete(100); //以异常的形式触发计算 //f.completeExceptionally(new Exception()); Thread.sleep(1000); }

3. 计算结果完成时的处理

public CompletableFuture
whenComplete(BiConsumer
action)public CompletableFuture
whenCompleteAsync(BiConsumer
action)public CompletableFuture
whenCompleteAsync(BiConsumer
action, Executor executor)public CompletableFuture
exceptionally(Function
fn)

上面4个方法是当计算阶段结束的时候触发,BiConsumer有两个入参,分别代表计算返回值,另外一个是异常.无返回值.方法不以Async结尾,意味着Action使用相同的线程执行,而Async可能会使用其它的线程去执行(如果使用相同的线程池,也可能会被同一个线程选中执行)。

future.whenCompleteAsync((v,e)->{       System.out.println("return value:"+v+"  exception:"+e); });
  • handle()
public  CompletableFuture     handle(BiFunction
fn)public CompletableFuture handleAsync(BiFunction
fn)public CompletableFuture handleAsync(BiFunction
fn, Executor executor)

whenComplete()不同的是这个函数返回CompletableFuture并不是原始的CompletableFuture返回的值,而是BiFunction返回的值.

4. CompletableFuture的组合

  • thenApply
    当计算结算完成之后,后面可以接继续一系列的thenApply,来完成值的转化.
public  CompletableFuture     thenApply(Function
fn)public CompletableFuture thenApplyAsync(Function
fn)public CompletableFuture thenApplyAsync(Function
fn, Executor executor)

它们与handle方法的区别在于handle方法会处理正常计算值和异常,因此它可以屏蔽异常,避免异常继续抛出。而thenApply方法只是用来处理正常值,因此一旦有异常就会抛出。

CompletableFuture
future = CompletableFuture.supplyAsync(()->{ return "hello world"; });CompletableFuture
future3 = future.thenApply((element)->{ return element+" addPart"; }).thenApply((element)->{ return element+" addTwoPart"; }); System.out.println(future3.get());//hello world addPart addTwoPart

5. CompletableFuture的Consumer

只对CompletableFuture的结果进行消费,无返回值,也就是最后的CompletableFuture是void.

public CompletableFuture
thenAccept(Consumer
action)public CompletableFuture
thenAcceptAsync(Consumer
action)public CompletableFuture
thenAcceptAsync(Consumer
action, Executor executor)
//入参为原始的CompletableFuture的结果.CompletableFuture future4 = future.thenAccept((e)->{            System.out.println("without return value");        });future4.get();
  • thenAcceptBoth

这个方法用来组合两个CompletableFuture,其中一个CompletableFuture等待另一个CompletableFuture的结果.

CompletableFuture
future = CompletableFuture.supplyAsync(()->{ return "hello world"; });CompletableFuture future5 = future.thenAcceptBoth(CompletableFuture.completedFuture("compose"), (x, y) -> System.out.println(x+y));//hello world compose

6. Either和ALL

    thenAcceptBoth是当两个CompletableFuture都计算完成,而我们下面要了解的方法applyToEither是当任意一个CompletableFuture计算完成的时候就会执行。

Random rand = new Random();        CompletableFuture
future9 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000 + rand.nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } return 100; }); CompletableFuture
future10 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000 + rand.nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } return 200; }); //两个中任意一个计算完成,那么触发Runnable的执行 CompletableFuture
f = future10.applyToEither(future9,i -> i.toString()); //两个都计算完成,那么触发Runnable的执行 CompletableFuture f1 = future10.acceptEither(future9,(e)->{ System.out.println(e); }); System.out.println(f.get());

    如果想组合超过2个以上的CompletableFuture,allOfanyOf可能会满足你的要求.allOf方法是当所有的CompletableFuture都执行完后执行计算。anyOf方法是当任意一个CompletableFuture执行完后就会执行计算,计算的结果相同。

总结

    有了CompletableFuture之后,我们自己实现异步编程变得轻松很多,这个类也提供了许多方法来组合CompletableFuture.结合Lambada表达式来用,变得很轻松.

 

转载于:https://my.oschina.net/u/136848/blog/2243556

你可能感兴趣的文章
开源 java CMS - FreeCMS2.3字典管理
查看>>
block,inline和inline-block概念和区别
查看>>
移动端常见随屏幕滑动顶部固定导航栏背景色透明度变化简单jquery特效
查看>>
javascript继承方式详解
查看>>
lnmp环境安装sh脚本
查看>>
白话讲反射技术 --- 适合初学者入门引导
查看>>
css变形 transform
查看>>
win7家庭版添加组策略编辑器
查看>>
iOS 一个开发者账号 多台Mac 共用
查看>>
lnmp环境搭建
查看>>
3.JUC之volatile
查看>>
oracle:win7手工卸载oracle数据库11g
查看>>
自定义session扫描器精确控制session销毁时间--学习笔记
查看>>
基于busybox搭建功能完善的小型linux(一)
查看>>
android The project target (Android 2.3.3) was not properly loaded
查看>>
【转】EDK简单使用流程(3)
查看>>
loj#2538. 「PKUWC2018」Slay the Spire
查看>>
这是一篇被河蟹了的博客
查看>>
一个两年Java的面试总结
查看>>
软件工程项目组Z.XML会议记录 2013/11/27
查看>>