CompletableFuture 详解

JAVA8之前的Future

public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        Future<String> future = executorService.submit(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "callable finished";
        });
        //do something else
        Thread.sleep(2000);
        String callableResult = future.get();
        System.out.println(callableResult);
    }

CompletableFuture的优势

  1. 提供了异步程序执行的另一种方式:回调,不需要像future.get()通过阻塞线程来获取异步结果或者通过isDone来检测异步线程是否完成来执行后续程序。
  2. 能够管理多个异步流程,并根据需要选择已经结束的异步流程返回结果。

构建CompletableFuture

构造函数

 /**
     * Creates a new incomplete CompletableFuture.
     */
    public CompletableFuture() {
    }

纯翻译:构建一个不完整的CompletableFuture,为什么说不完整呢,请往下看

式例1

public static class test{
        public static String getTestResult()
        {
            int i = 10/0;
            return "test";
        }
    }
    public static void main(String[] args) {

        CompletableFuture<String> completableFuture  = new CompletableFuture();
        new Thread(()->{
            try {
                completableFuture.complete(test.getTestResult());
            } catch (Exception e) {
                System.out.println("get exception in side");
                completableFuture.completeExceptionally(e);
            }
        }).start();

        try {
            String result = completableFuture.get();
            System.out.println(result);
        } catch (Exception e) {
            System.out.println("get exception out side");
            e.printStackTrace();
        }
    }

一般需要complete() 设置异步线程可能返回的值,以及completeExceptionally() 向上抛出异常

工厂方法

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
                                                       Executor executor)
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable,
                                                   Executor executor)

从入参可以看到,CompletableFuture 允许我们自定义执行器,在实际项目中我们可以选择合适的线程池来提高异步程序的效率。

    CompletableFuture completableFuture = CompletableFuture.supplyAsync(() ->
                { 
                    try {Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return "test";
                }
        );

CompletableFuture 流

java8 流式处理在CompletableFuture 也得到了完美的体现,流处理包含的中间操作,终端操作分别对应CompletableFuture 中以thenAccept开头返回CompletableFuture <Void>(也就是回调)的实例方法。中间操作对应thenApply,thenCompose等等返回非CompletableFuture <Void>的实例方法。

式例2

 CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1);
                System.out.println(Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "test ";
        }).thenApply(u -> {
            System.out.println(Thread.currentThread().getName());
            return u + "in thenApply first";
        })
                .thenCompose(u -> CompletableFuture.supplyAsync(() -> {
                            System.out.println(Thread.currentThread().getName());
                            return u + "in thenCompose second";
                        })
                ).thenAccept(u -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println(u + "in thenAccept last");
        });
ForkJoinPool.commonPool-worker-1
main
ForkJoinPool.commonPool-worker-1
main
test in thenApply firstin thenCompose secondin thenAccept last

可以看到默认的异步线程池都是ForkJoinPool.commonPool,同步操作都在main线程中处理。
多说一句thenApply 和thenCompose的区别,thenCompose在调用外部接口返回CompletableFuture<>类型时更方便。

多个CompletableFuture任务的管理

现实应用中可能同时存在多个异步任务,有时候我们需要他们一起完成才能进行下面的操作,有时候我们又只需要在存在一个结果的情况下就返回。

   private static  final Random random = new Random();
    public static  String randomDelay()
    {
        int delay = 500 + random.nextInt(2000);
        try {
            System.out.println(String.format("%s sleep in %d",Thread.currentThread().getName(),delay));
            Thread.sleep(delay);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(String.format("%s sleep in %s",Thread.currentThread().getName(),"end"));
        return Thread.currentThread().getName()+" return";
    }
    public static void main(String[] args) {
         CompletableFuture [] futures = {CompletableFuture.supplyAsync(()->randomDelay()),
                 CompletableFuture.supplyAsync(()->randomDelay()),CompletableFuture.supplyAsync(()->randomDelay())};
        CompletableFuture.allOf(futures).join();
        System.out.println("all timeout process end");
    }
ForkJoinPool.commonPool-worker-2 sleep in 1957
ForkJoinPool.commonPool-worker-3 sleep in 2097
ForkJoinPool.commonPool-worker-1 sleep in 2422
ForkJoinPool.commonPool-worker-2 sleep in end
ForkJoinPool.commonPool-worker-3 sleep in end
ForkJoinPool.commonPool-worker-1 sleep in end
all timeout process end

上段代码展示了 CompletableFuture.allOf 的用法,可以看到所有的线程结束后打印了"all timeout process end",注意 allOf 接受的是数组类对象。如果把allOf改为 anyOf

CompletableFuture [] futures = {CompletableFuture.supplyAsync(()->randomDelay()),
                 CompletableFuture.supplyAsync(()->randomDelay()),CompletableFuture.supplyAsync(()->randomDelay())};
        System.out.println(CompletableFuture.anyOf(futures).get());
        System.out.println("all timeout process end");
ForkJoinPool.commonPool-worker-2 sleep in 529
ForkJoinPool.commonPool-worker-3 sleep in 759
ForkJoinPool.commonPool-worker-1 sleep in 1750
ForkJoinPool.commonPool-worker-2 sleep in end
ForkJoinPool.commonPool-worker-2 return
all timeout process end

可以看到只有一个线程结束时结果已经返回,另外CompletableFuture还提供了专为两个任务处理的方法
acceptEither

 CompletableFuture<String> completableFuture  = CompletableFuture.supplyAsync(()->randomDelay());
        completableFuture.acceptEither(completableFuture.supplyAsync(()->randomDelay()),u-> System.out.println(u)).join();
ForkJoinPool.commonPool-worker-2 sleep in 935
ForkJoinPool.commonPool-worker-1 sleep in 2422
ForkJoinPool.commonPool-worker-2 sleep in end
ForkJoinPool.commonPool-worker-2 return

CompletableFuture 异常处理

除了在get的时候通过 try catch 处理异常,CompletableFuture 提供了更优雅的方式 exceptionally()和 handle()。handle处理方法类似,都是把异常对象转为我们所需要的其他类型对象,然后处理。

  public static String getTestResult()
    {
        int i = 10/0;
        return "test";
    }

    public static void main(String[] args) {
        CompletableFuture<String> completableFuture  = new CompletableFuture();
        new Thread(()->{
            try {
                completableFuture.complete(getTestResult());
            } catch (Exception e) {
                System.out.println("get exception in side");
                completableFuture.completeExceptionally(e);
            }
        }).start();
        completableFuture.exceptionally(e->"we hava a exception"+e.getMessage())
                .thenAccept(u-> System.out.println(u));
    }

总结

有关CompletableFuture 大部分内容已经讲完了,相信看了之后应该都会用了吧!
更多的细节还是参考JAVA8官方文档和源码吧!

http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • CompletableFuture类实现了CompletionStage和Future接口。Future是Java...
    数齐阅读 62,684评论 9 82
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,293评论 18 399
  • 这些天我比较忙!忙着自制中药膏,因为科里有一个糖足的患者,他的脚丫子实在是让我头疼,听说临漳有个治脚不错的,我去看...
    灵魂深处是无言阅读 210评论 0 0
  • 今天的我终于出门了,心情好了许多,每次跟姐姐们在一起,我觉得我就能学到好多东西,通过听她们的聊天,我觉得我还是太...
    简爱De阅读 156评论 0 0