异步任务之CompletableFuture

前言

在实际开发过程中,难免会遇到诸如文件上传、下载等耗时且用户不需要重点关注的需求,让用户一直等待操作完成显然不是最优解决方案,这时就需要我们开启一个异步任务去处理后台任务,让用户不再等待,下面,从原始的Future接口说起,逐步深入了解CompletableFuture接口的相关特性和应用。

一、Future回顾

Future接口是jdk5提出的异步任务解决方案,定义了操作异步任务执行一些方法,例如检查计算是否完成、等待计算完成以及检索计算结果的方法等。
在这里插入图片描述
异步任务有三个特点:多线程、有返回、异步,因此,想要实现异步任务,还需要多线程处理,这就涉及到了Runable接口和Callable接口,我们直到,Callable接口是有返回的,因此,我们在开启异步任务时,创建的线程是声明Callable接口的,下面,看下Future接口的实现FutureTask的相关引用
在这里插入图片描述
在上图中,FutureTask实现了Runable相关接口,和我们上面的说法冲突,难道上述说法不正确吗?看下FutureTask的构造器,有两个

package com.example.test.CompletableFuture;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class FutureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask = new FutureTask<>(new Thread1());

        new Thread(()->{
            futureTask.run();
        },"t1").start();
        System.out.println(Thread.currentThread().getName()+"执行其他任务");
        System.out.println(Thread.currentThread().getName()+":"+futureTask.get());
    }
}

class Thread1 implements Callable{
    @Override
    public Object call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"-----------get-----------");
        return "hello world!";
    }
}

创建一个实现callable接口的线程t1,让它去执行任务,然后获取它的结果,运行结果如下
在这里插入图片描述
可以看到,执行异步任务的线程和打印结果的线程不是同一个,主线程去执行其他任务了,那么,如果我们有多个任务,是不是每次都要开启一个线程去执行呢?显然不是的,频繁的开启线程会带来额外的开销,因此,我们使用线程池去处理,加入我们有三个任务,先看下顺序执行的耗时

public static void m1(){
        //一个主线程,执行3个任务时间
        long startTime = System.currentTimeMillis();
        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("----costTime:"+(endTime-startTime)+"毫秒-----");
    }

在这里插入图片描述
然后我们创建一个3个线程的定长线程池,这里为了方便,我是用Executors类创建,实际应用线程池不推荐这样操作

public static void m2() throws ExecutionException, InterruptedException {
        long startTime = System.currentTimeMillis();
        //多个异步线程执行任务
        FutureTask<String> futureTask1 = new FutureTask<String>(()->{
            try {
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "task1 over";
        });

        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        threadPool.submit(futureTask1);

        FutureTask<String> futureTask2 = new FutureTask<String>(()->{
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "task2 over";
        });
        threadPool.submit(futureTask2);

        FutureTask<String> futureTask3 = new FutureTask<String>(()->{
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "task3 over";
        });
        threadPool.submit(futureTask3);

        //get方法阻塞主线程执行,一般放在程序主线程后面,也可以设置等待超时时间
        //通常使用轮询方式去查询结果,通过isDone()方法去判断异步任务是否执行完成,执行完成再去get()
        System.out.println(futureTask1.get());
        System.out.println(futureTask2.get());
        System.out.println(futureTask3.get());
        long endTime = System.currentTimeMillis();
        System.out.println("----costTime:"+(endTime-startTime)+"毫秒-----");
        System.out.println(Thread.currentThread().getName());
        threadPool.shutdown();
    }

在这里插入图片描述
可以看到,耗时大大缩短,这也就看出Future的优点:Future+线程池异步多线程任务配合,能显著提高程序的运行效率。请注意上面代码的中,我获取执行结果统一放到了最后,这是因为get()方法,会阻塞线程的执行,直到获取结果,如果每个任务后面紧挨着获取结果,那就变成了顺序任务,执行时间和m1()无异。那么,怎么可以判断异步任务是否执行完成呢?Future接口中提供了isDone()方法,我们可以轮询isDone()去判断是否执行完成,可是这样就会造成资源浪费,由此引出了Future接口的两个问题:

  • get()阻塞—一旦调用get()方法求结果,一旦调用不见不散,非要等到结果才会离开,不管你是否计算完成,如果没有计算完成容易程序堵塞
  • isDone()轮询—轮询的方式会耗费无谓的cpu资源,而且也不见得能及时得到计算结果,如果想要异步获取结果,通常会以轮询的方式去获取结果,尽量不要阻塞
    可以看到,Future对于结果的获取不是很友好,只能通过阻塞或轮询的方式得到任务的结果,那么有没有一种更好的方法去获取结果呢?那就是今天的主角CompletableFuture。

二、CompletableFuture入门

先看下CompletableFuture的类图
在这里插入图片描述
可以看到它实现了Future接口和CompletionStage接口,实现Future接口我们很容易理解,那么CompletionStage是做什么的呢?上面我们说过Future不能应付任务完成通知(非轮询)、多个任务依赖组合处理(将多个异步任务结果组合起来,后任务依赖前任务结果)等复杂场景,而CompletableFuture可以实现如下功能:

  • 异步任务结束,会自动回调某个对象方法
  • 主线程设置好回调后,不再关心异步任务的执行,异步任务之间可以顺序执行
  • 异步任务出错时,会自动回调某个对象方法
    它之所以这么强大,就是因为实现了CompletionStage接口功能。下面,先看下ConpletableFuture如何使用,和FutureTask不同的是,CompletableFuture不是通过new的方式操作的,它提供了四个静态方法初始化,分为两大类:runAsync(无返回值)和supplyAsync(有返回值),分别无线程池形参和有线程池形参
/**
     * 根据四个静态方法创建,两大类:runAsync(无返回值)和supplyAsync(有返回值),分别无线程池形参和有线程池形参
     * public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
     *     return asyncSupplyStage(asyncPool, supplier);
     * }
     *
     * public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
     *                                                    Executor executor) {
     *     return asyncSupplyStage(screenExecutor(executor), supplier);
     * }
     *
     * public static CompletableFuture<Void> runAsync(Runnable runnable) {
     *     return asyncRunStage(asyncPool, runnable);
     * }
     *
     * public static CompletableFuture<Void> runAsync(Runnable runnable,
     *                                                Executor executor) {
     *     return asyncRunStage(screenExecutor(executor), runnable);
     * }
     */
    public static void init() throws ExecutionException, InterruptedException {
        /**
         * 无返回值
         */
        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(()->{
            try {
                System.out.println(Thread.currentThread().getName());
                TimeUnit.MILLISECONDS.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        System.out.println(voidCompletableFuture.get());

        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
            try {
                System.out.println(Thread.currentThread().getName());
                TimeUnit.MILLISECONDS.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },threadPool);
        System.out.println(completableFuture.get());
        threadPool.shutdown();

        /**
         * 有返回值
         */

        CompletableFuture<String> objectCompletableFuture = CompletableFuture.supplyAsync(()->{
            try {
                System.out.println(Thread.currentThread().getName());
                TimeUnit.MILLISECONDS.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "hello world!";
        });
        System.out.println(objectCompletableFuture.get());


        ExecutorService threadPool1 = Executors.newFixedThreadPool(3);
        CompletableFuture<String> objectCompletableFuture1 = CompletableFuture.supplyAsync(()->{
            try {
                System.out.println(Thread.currentThread().getName());
                TimeUnit.MILLISECONDS.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "hello world!";
        },threadPool1);
        System.out.println(objectCompletableFuture1.get());
        threadPool1.shutdown();
    }

既然CompletableFuture实现了Future接口,那么如何操作CompletableFuture作为Future接口使用呢?

public static void use() throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "--------come in");
            int i = ThreadLocalRandom.current().nextInt(10);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("----------1秒钟后出结果:" + i);
            return i;
        });
        System.out.println(Thread.currentThread().getName()+"线程先去忙别的任务");
        System.out.println(integerCompletableFuture.get());
    }

在这里插入图片描述
接着看下CompletableFuture的通知和承上启下如何实现

public static void notifyResult(){
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        try{
            CompletableFuture.supplyAsync(() -> {
                System.out.println(Thread.currentThread().getName() + "--------come in");
                int i = ThreadLocalRandom.current().nextInt(10);
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("----------3秒钟后出结果:" + i);
                /**
                 * 模拟异常
                 */
//                if(i>1){
//                    int r = 10/0;
//                }
                return i;
            },threadPool).whenComplete((re,ex)->{
                /**
                 * re代表上一步任务的值
                 * ex代表上一步是否有异常
                 */
                if(ex == null){
                    System.out.println("----计算完成,更新系统值:"+re);
                }
            }).exceptionally(ex->{
                ex.printStackTrace();
                System.out.println("异常情况:"+ex.getCause()+"\t"+ex.getMessage());
                return null;
            });
            System.out.println(Thread.currentThread().getName()+"线程先去忙别的任务");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
        /**
         * 主线程不要立即结束,否则CompletableFuture默认使用线程池会立刻关闭,不能返回结果,或者使用自定义线程池
         */
//        try {
//            TimeUnit.SECONDS.sleep(3);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }
    }

在这里插入图片描述
我们可以看到,在异步任务后面有一个whenComplete方法,它有两个参数,分别为执行结果和异常,我们获取上一个异步任务的结果就是通过这个方法实现,同时,根据有无异常,我们还可以调用不同的回调方法。这里有一个注意的点是,我传入了自定义的线程池,这是因为,不传入自定义线程池的话,假如主线程立即结束,CompletableFuture默认使用线程池会立刻关闭,不能返回结果,因此,不传入自定义线程池的话,需要让主线程等待一段时间,这样才能获取返回结果。

三、常用API

CompletableFuture的API很多,基本上可以分为六类,接下来分别简单介绍。

  • 获取结果和触发计算
    获取结果我们并不陌生,已经使用过的get()方法就是获取结果,除此之外,还有get(long timeout,TimeUnit unit),超过等待时间抛出异常;join(),同get方法,不用抛异常;getNow(T valueIfAbsent)获取结果的方法,这里我们着重介绍getNow方法。而触发计算是complete(T value)方法,返回true/false,代表是否中断了执行中的异步任务,其中,入参value代表若中断了异步任务,返回一个设定值value
public static void m1() throws ExecutionException, InterruptedException, TimeoutException {
        CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "abc";
        });
        //get
//        System.out.println(stringCompletableFuture.get());
//        System.out.println(stringCompletableFuture.get(2L,TimeUnit.SECONDS));
//        System.out.println(stringCompletableFuture.join());

        /**
         * getNow():立即获取结果不阻塞
         * 1、计算完成,返回计算完成的结果
         * 2、没计算完,返回设定的valueIfAbsent值
         */

//        try {
//            TimeUnit.SECONDS.sleep(3);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }
//        System.out.println(stringCompletableFuture.getNow("xxx"));

        /**
         * complete(T value)方法返回true/false,代表是否中断了执行中的异步任务,其中,入参value代表若中断了异步任务,返回一个设定值value
         */

        System.out.println(stringCompletableFuture.complete("test")+"\t"+stringCompletableFuture.join());
    }
  • 对计算结果进行处理:计算结果存在依赖关系,线程串行化
    主要分为两类,thenApply和handle,他们分别有三种实现,主要区别就是,thenApply遇到异常就会停止在出现异常的任务结束,而handle可以处理遇到的异常,继续向下执行,然而由于任务异常,会导致计算过程丢失,因此最终的结果是null
* thenApply:
     *  public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
     *  public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
     *  public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
     * handle:
     *  public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
     *  public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn)
     *  public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor)
     */
public static void m2(){
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        CompletableFuture.supplyAsync(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("买鱼");
            return 1;
        },threadPool).thenApply(f->{
            System.out.println("起锅烧油");
            return f+2;
        })


//                .handle((f,e)->{
//                    //遇到异常,继续向下执行
//                    int i = 10/0;
//                    System.out.println("起锅烧油");
//                    return f+2;
//                })
//                .handle((f,e)->{
//                    System.out.println("起锅烧油");
//                    return f+2;
//                })


//                .thenApply(f->{
//                    //遇到异常,终止运行
//                    int i = 10/0;
//                    System.out.println("起锅烧油");
//                    return f+2;
//        })
                .thenApply(f->{
            System.out.println("做酸菜鱼");
            return f+3;
        }).whenComplete((re,ex)->{
            if(null==ex){
                System.out.println("计算结果:"+re);
            }
        }).exceptionally(ex->{
            ex.printStackTrace();
            return null;
        });
        threadPool.shutdown();
        System.out.println(Thread.currentThread().getName()+" 去忙其他的");
    }

在这里插入图片描述

  • 对计算结果进行消费:接受任务的处理结果,并消费结果,无返回结果
	 * thenRun:任务A执行完执行任务B,B不需要A的结果
     * thenAccept:任务A执行完执行任务B,B需要A的结果,但是任务B无返回
     * thenApply:任务A执行完执行任务B,B需要A的结果,任务B有返回
    public static void m3(){
//        CompletableFuture.supplyAsync(()->{
//            return 1;
//        }).thenApply(f->{
//            return f+1;
//        }).thenApply(f->{
//            return f+2;
//        }).thenAccept(r->{
//            System.out.println(r);
//        });

        /**
         * null
         */
//        System.out.println(CompletableFuture.supplyAsync(()->{
//            return "resultA";
//        }).thenRun(()->{
//
//        }).join());


        /**
         * resultA
         * null
         */
//        System.out.println(CompletableFuture.supplyAsync(()->{
//            return "resultA";
//        }).thenAccept(f->{
//            System.out.println(f);
//        }).join());


        /**
         * resultA resultB
         */
//        System.out.println(CompletableFuture.supplyAsync(()->{
//            return "resultA";
//        }).thenApply(r->{
//            return r+" resultB";
//        }).join());

    }
  • CompletableFuture与线程池
    这里先说结论,下面的代码可以自己运行,看下结果是否和作者一致
     * 1、没有传入自定义线程池,都用默认线程池ForkJoinPool
     * 2、传入了自定义线程池:执行第一个任务时传入自定义线程池
     *  -调用thenRun、thenAccept、thenApply等方法,执行剩余任务共用一个线程池
     *  -调用thenRunAsync、thenAcceptAsync、thenApplyAsync等方法,第一个任务使用自定线程池,其余任务使用ForkJoin线程池
     * 3、备注:有可能处理太快,系统优化切换原则,直接使用main线程处理
public static void m4(){

        /**
         * 1号任务:ForkJoinPool.commonPool-worker-9
         * 2号任务:ForkJoinPool.commonPool-worker-9
         * 3号任务:ForkJoinPool.commonPool-worker-9
         * 4号任务:ForkJoinPool.commonPool-worker-9
         * 5号任务:ForkJoinPool.commonPool-worker-9
         * null
         */
//        ExecutorService threadPool = Executors.newFixedThreadPool(5);
//        try{
//            CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(10);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                System.out.println("1号任务:" + Thread.currentThread().getName());
//                return "111";
//            }).thenRun(() -> {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(10);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                System.out.println("2号任务:" + Thread.currentThread().getName());
//            }).thenRun(() -> {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(20);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                System.out.println("3号任务:" + Thread.currentThread().getName());
//            }).thenRun(() -> {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(10);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                System.out.println("4号任务:" + Thread.currentThread().getName());
//            }).thenRun(() -> {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(10);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                System.out.println("5号任务:" + Thread.currentThread().getName());
//            });
//            System.out.println(voidCompletableFuture.get(2L,TimeUnit.SECONDS));
//        }catch (Exception e){
//            e.printStackTrace();
//        }finally {
//            threadPool.shutdown();
//        }


        /**
         * 1号任务:ForkJoinPool.commonPool-worker-9
         * 2号任务:ForkJoinPool.commonPool-worker-9
         * 3号任务:ForkJoinPool.commonPool-worker-9
         * 4号任务:ForkJoinPool.commonPool-worker-9
         * 5号任务:ForkJoinPool.commonPool-worker-9
         * null
         */
//        ExecutorService threadPool = Executors.newFixedThreadPool(5);
//        try{
//            CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(10);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                System.out.println("1号任务:" + Thread.currentThread().getName());
//                return "111";
//            }).thenRunAsync(() -> {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(10);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                System.out.println("2号任务:" + Thread.currentThread().getName());
//            }).thenRunAsync(() -> {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(20);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                System.out.println("3号任务:" + Thread.currentThread().getName());
//            }).thenRunAsync(() -> {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(10);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                System.out.println("4号任务:" + Thread.currentThread().getName());
//            }).thenRunAsync(() -> {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(10);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                System.out.println("5号任务:" + Thread.currentThread().getName());
//            });
//            System.out.println(voidCompletableFuture.get(2L,TimeUnit.SECONDS));
//        }catch (Exception e){
//            e.printStackTrace();
//        }finally {
//            threadPool.shutdown();
//        }


        /**
         * 1号任务:pool-1-thread-1
         * 2号任务:pool-1-thread-1
         * 3号任务:pool-1-thread-1
         * 4号任务:pool-1-thread-1
         * 5号任务:pool-1-thread-1
         * null
         */
//        ExecutorService threadPool = Executors.newFixedThreadPool(5);
//        try{
//            CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(10);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                System.out.println("1号任务:" + Thread.currentThread().getName());
//                return "111";
//            },threadPool).thenRun(() -> {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(10);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                System.out.println("2号任务:" + Thread.currentThread().getName());
//            }).thenRun(() -> {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(20);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                System.out.println("3号任务:" + Thread.currentThread().getName());
//            }).thenRun(() -> {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(10);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                System.out.println("4号任务:" + Thread.currentThread().getName());
//            }).thenRun(() -> {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(10);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//                System.out.println("5号任务:" + Thread.currentThread().getName());
//            });
//            System.out.println(voidCompletableFuture.get(2L,TimeUnit.SECONDS));
//        }catch (Exception e){
//            e.printStackTrace();
//        }finally {
//            threadPool.shutdown();
//        }


        /**
         * 1号任务:pool-1-thread-1
         * 2号任务:ForkJoinPool.commonPool-worker-9
         * 3号任务:ForkJoinPool.commonPool-worker-9
         * 4号任务:ForkJoinPool.commonPool-worker-9
         * 5号任务:ForkJoinPool.commonPool-worker-9
         * null
         */
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        try{
            CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("1号任务:" + Thread.currentThread().getName());
                return "111";
            },threadPool).thenRunAsync(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("2号任务:" + Thread.currentThread().getName());
            }).thenRunAsync(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(20);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("3号任务:" + Thread.currentThread().getName());
            }).thenRunAsync(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("4号任务:" + Thread.currentThread().getName());
            }).thenRunAsync(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("5号任务:" + Thread.currentThread().getName());
            });
            System.out.println(voidCompletableFuture.get(2L,TimeUnit.SECONDS));
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }
  • 对计算速度进行选用:多个任务,看谁先执行完成
public static void m5(){
        CompletableFuture<String> playA = CompletableFuture.supplyAsync(() -> {
            System.out.println("A come in---------");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "playA";
        });

        CompletableFuture<String> playB = CompletableFuture.supplyAsync(() -> {
            System.out.println("B come in---------");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "playB";
        });
        System.out.println(Thread.currentThread().getName()+": "+playA.applyToEither(playB, f -> {
            return f + " is winner";
        }).join());
    }

在这里插入图片描述

  • 对计算结果合并
public static void m6(){
        CompletableFuture<Integer> integerCompletableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t -----------启动");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return 10;
        });

        CompletableFuture<Integer> integerCompletableFuture2 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t -----------启动");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return 50;
        });

        System.out.println(integerCompletableFuture1.thenCombine(integerCompletableFuture2, (x, y) -> {
            System.out.println("开始结果合并");
            return x + y;
        }).join());
    }

在这里插入图片描述

四、实际应用

有一个需求,比较各大网站或者是某一网站不同商家某个产品的定价,那么我们可以想到的解决方法大致两种

  • 挨个查询每家的售价
  • 做成异步任务,然后汇总
    我们先定义一个商家类和商家列表
class NetMall{
    @Getter
    private String netMallName;

    public NetMall(String netMallName){
        this.netMallName = netMallName;
    }

    public BigDecimal calculatePrice(String productName){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return BigDecimal.valueOf(ThreadLocalRandom.current().nextDouble()*2.5 + productName.charAt(2));
    }
}
static List<NetMall> list = Arrays.asList(
            new NetMall("jd"),
            new NetMall("tb"),
            new NetMall("mt")
    );
public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        List<String>list1 = getPrice1(list,"mysql");
        for (String s : list1) {
            System.out.println(s);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("----costTime:"+(endTime-startTime)+"毫秒-----");
    }

然后,分别实现上述两种方式,看差距有多少

public static List<String> getPrice1(List<NetMall> list,String productName){
        return list.stream().map(netMall -> String.format(productName + " in %s price is %.2f", netMall.getNetMallName(), netMall.calculatePrice(productName))).collect(Collectors.toList());
    }

上面用到了链式调用,如果不熟悉的话,可以先去了解一下,这是按部就班的执行方式,看下耗时
在这里插入图片描述
然后我们采用异步任务处理

public static List<String> getPrice2(List<NetMall> list,String productName){
        return list.stream().map(netMall ->
                        CompletableFuture.supplyAsync(() -> String.format(productName + " in %s price is %.2f",
                                netMall.getNetMallName(),
                                netMall.calculatePrice(productName))))
                .collect(Collectors.toList())
                .stream()
                .map(s -> s.join())
                .collect(Collectors.toList());
    }
public static List<String> getPrice3(List<NetMall> list,String productName){
        List<CompletableFuture<String>> stringList = new ArrayList<>();
        List<String>result = new ArrayList<>();
        ExecutorService threadPool = Executors.newFixedThreadPool(list.size());
        for (NetMall netMall : list) {
//             stringList.add(CompletableFuture.supplyAsync(() -> String.format(productName + " in %s price is %.2f",
//                     netMall.getNetMallName(),
//                     netMall.calculatePrice(productName))));


            stringList.add(CompletableFuture.supplyAsync(()->{
                System.out.println(Thread.currentThread().getName()+" come in--------------");
                return String.format(productName + " in %s price is %.2f",
                        netMall.getNetMallName(),
                        netMall.calculatePrice(productName));
            },threadPool));
        }
        for (CompletableFuture<String> stringCompletableFuture : stringList) {
            result.add(stringCompletableFuture.join());
        }
        threadPool.shutdown();
        return result;
    }

这里给出了链式调用和普通写法,可以先看普通写法理解从哪里转换到哪里,再去看链式调用,就比较清晰了
在这里插入图片描述
可以看到,耗时差距耗时很大的,如果要执行的任务很多,异步任务的优势就很大了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雅俗共赏zyyyyyy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值