CompletableFuture实现微服务优雅调用

使用CompletableFuture实现微服务优雅调用。

先简单介绍几个常用方法:

get()

阻塞当前CompletableFuture的线程,直到异步调用返回结果,需要手动抛出异常throws InterruptedException, ExecutionException

join()

同get(),不需要手动抛出异常

allOf()

通过allOf把多个任务连接到一起,调用join()阻塞,直到所有任务都执行完才可继续执行。

thenApply()

接收任务的返回结果作为输入参数,方法内部执行一些操作后,并把任务的返回结果输出。

thenAccept()

接收任务的返回结果作为输入参数,方法内部执行一些操作后,不输出结果。

thenRun()

不接受参数,也不输出结果。只在方法内部执行一些操作后

示例

通过allOf方法来实现多个调用的统一返回。

提交10个任务,并获取任务结果进行测试。

		//模拟10个任务数据
        Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        List<CompletableFuture<Integer>> futures = new ArrayList<>();
        System.out.println("程序开始=======");

        //提交10个任务
        Arrays.stream(arr).collect(Collectors.toList()).stream().forEach(a -> {
            CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
                try {
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println("执行任务ID={}, 当前时间="+ System.currentTimeMillis()/1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return a;
            });
            futures.add(integerCompletableFuture);
        });

        //阻塞
        System.out.println("CompletableFuture 之前");
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        System.out.println("CompletableFuture 之后");

        //打印10个任务结果
        long start = System.currentTimeMillis();
        futures.forEach(a -> {
            System.out.println("任务结果:" + a.join());

        });
        System.out.println("打印10个任务花费时间:" + (System.currentTimeMillis() - start)/1000);

在这里我们可以将对各future实例添加到allOf方法中,然后通过future的join()获取future的状态。如果allOf里面的所有线程为执行完毕,主线程会阻塞,直到allOf里面的所有线程都执行,线程就会被唤醒。

打印结果:

程序开始=======
CompletableFuture 之前
执行任务ID={}, 当前时间=1594979442
执行任务ID={}, 当前时间=1594979442
执行任务ID={}, 当前时间=1594979442
执行任务ID={}, 当前时间=1594979442
执行任务ID={}, 当前时间=1594979442
执行任务ID={}, 当前时间=1594979442
执行任务ID={}, 当前时间=1594979442
执行任务ID={}, 当前时间=1594979444
执行任务ID={}, 当前时间=1594979444
执行任务ID={}, 当前时间=1594979444
CompletableFuture 之后
任务结果:7
任务结果:2
任务结果:6
任务结果:8
任务结果:10
任务结果:3
任务结果:5
任务结果:4
任务结果:1
任务结果:9
打印10个任务花费时间:0

打印的时间戳转为了秒,可以看到前7条一起执行,过了两秒,又执行了3条。
这是因为我的电脑是4核8线程,而CompletableFuture的线程池是ForkJoinPool.commonPool(),默认使用 CPU核心数-1个线程数,而为什么是8-1=7,而不是4-1=3呢?看下图你就明白了
CompletableFuture实现微服务优雅调用_第1张图片
从CompletableFuture的supplyAsync()方法,分析默认线程池:

  1. 用到了asyncPool
	public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
       return asyncSupplyStage(asyncPool, supplier);
    }
  1. ForkJoinPool的commonPool
    private static final Executor asyncPool = useCommonPool ?
        ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
  1. makeCommonPool()的源码省略,直接看重点:
    parallelism = Runtime.getRuntime().availableProcessors() - 1。
    即:Java虚拟机的可用的处理器数量 - 1。
    如果parallelism -1 < 0,则默认使用1个线程。
    private static ForkJoinPool makeCommonPool() {
        ...
        ...
        if (parallelism < 0 && // default 1 less than #cores
            (parallelism = Runtime.getRuntime().availableProcessors() - 1) <= 0)
            parallelism = 1;
        if (parallelism > MAX_CAP)
            parallelism = MAX_CAP;
        return new ForkJoinPool(parallelism, factory, handler, LIFO_QUEUE,
                                "ForkJoinPool.commonPool-worker-");
    }

异步编程,不想阻塞线程的话,可以使用thenAccpt、thenApply、thenRun,future结束后,执行异步方法

CompletableFuture可以实现如下等功能

  • 转换(thenCompose)
  • 组合(thenCombine)
  • 消费(thenAccept)
  • 运行(thenRun)。
  • 带返回的消费(thenApply)

消费和运行的区别

消费使用执行结果。运行则只是运行特定任务。具体其他功能大家可以根据需求自行查看。

CompletableFuture借助CompletionStage的方法可以实现链式调用。并且可以选择同步或者异步两种方式。

你可能感兴趣的:(多线程)