美文网首页
Java8之CompletableFuture

Java8之CompletableFuture

作者: AC编程 | 来源:发表于2022-02-17 07:59 被阅读0次

一、Future计算

Future异步计算很难操作,通常我们希望将任何计算逻辑视为一系列步骤。但是在异步计算的情况下,表示为回调的方法往往分散在代码中或者深深地嵌套在彼此内部。但是当我们需要处理其中一个步骤中可能发生的错误时,情况可能会变得更复杂。

Futrue接口是Java 5中作为异步计算而新增的,但它没有任何方法去进行计算组合或者处理可能出现的错误。

当处理一个任务时,总会遇到以下几个阶段:

  • 提交任务

  • 执行任务

  • 任务完成的后置处理

1.1 Future示例代码

接下来我们先用最简单的例子迅速对 Future 有个直观理解。

1.1.1 Future异步执行,不需要取返回值

代码

public class FutureTest {
    /**
     * Future异步执行,不需要取返回值
     */
    public static void main(String[] args) {
        System.out.println(Thread.currentThread() + " 主线程-开始,time->" + getTime());

        ExecutorService executor = Executors.newFixedThreadPool(3);

        executor.submit(() -> {
            System.out.println("子线程-是否为守护线程:" + Thread.currentThread().isDaemon());
            System.out.println(Thread.currentThread() + " 子线程-开始,time->" + getTime());

            Thread.sleep(2000);

            System.out.println(Thread.currentThread() + " 子线程-退出,time->" + getTime());

            return "AlanChen";
        });

        System.out.println(Thread.currentThread() + " 主线程-退出,time->" + getTime());
    }

    private static String getTime() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        return formatter.format(new Date());
    }
}

执行结果

Thread[main,5,main] 主线程-开始,time->2022-02-16 16:44:29 719
子线程-是否为守护线程:false
Thread[main,5,main] 主线程-退出,time->2022-02-16 16:44:29 756
Thread[pool-1-thread-1,5,main] 子线程-开始,time->2022-02-16 16:44:29 756
Thread[pool-1-thread-1,5,main] 子线程-退出,time->2022-02-16 16:44:31 761
1.1.2 Future异步执行,取返回值

代码

public class FutureTest2 {
    /**
     * Future异步执行,取返回值
     */
    public static void main(String[] args) {
        System.out.println(Thread.currentThread() + " 主线程-开始,time->" + getTime());

        ExecutorService executor = Executors.newFixedThreadPool(3);

        Future future = executor.submit(() -> {
            System.out.println("子线程-是否为守护线程:" + Thread.currentThread().isDaemon());
            System.out.println(Thread.currentThread() + " 子线程-开始,time->" + getTime());

            Thread.sleep(2000);

            System.out.println(Thread.currentThread() + " 子线程-退出,time->" + getTime());

            return "AlanChen";
        });

        // 中间可以先执行一堆逻辑,再阻塞获取返回结果
        System.out.println("中间可以先执行一堆逻辑,再阻塞获取返回结果");

        try {
            //阻塞获取返回结果
            System.out.println("异步执行返回结果:" + future.get());
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread() + " 主线程-退出,time->" + getTime());
    }

    private static String getTime() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        return formatter.format(new Date());
    }
}

执行结果

Thread[main,5,main] 主线程-开始,time->2022-02-16 16:49:19 027
中间可以先执行一堆逻辑,再阻塞获取返回结果
子线程-是否为守护线程:false
Thread[pool-1-thread-1,5,main] 子线程-开始,time->2022-02-16 16:49:19 070
Thread[pool-1-thread-1,5,main] 子线程-退出,time->2022-02-16 16:49:21 079
异步执行返回结果:AlanChen
Thread[main,5,main] 主线程-退出,time->2022-02-16 16:49:21 080
1.2 Future 如何被构建的

Future 是如何被创建的呢? 生产者线程提交给消费者线程池任务时,线程池会构造一个实现了 Future 接口的对象 FutureTask 。该对象相当于是消费者和生产者的桥梁,消费者通过 FutureTask 存储任务的处理结果,更新任务的状态:未开始、正在处理、已完成等。而生产者拿到的 FutureTask 被转型为 Future 接口,可以阻塞式获取任务的处理结果,非阻塞式获取任务处理状态。以上两个示例代码,可以使用FutureTask来实现

1.2.1 FutureTask异步执行,不需要取返回值

代码

public class FutureTaskTest {
    /**
     * FutureTask异步执行,不需要取返回值
     */
    public static void main(String[] args) {
        System.out.println(Thread.currentThread() + " 主线程-开始,time->" + getTime());

        ExecutorService executor = Executors.newFixedThreadPool(3);

        executor.submit(new FutureTask<>(() -> {
            System.out.println("子线程-是否为守护线程:" + Thread.currentThread().isDaemon());
            System.out.println(Thread.currentThread() + " 子线程-开始,time->" + getTime());

            Thread.sleep(2000);

            System.out.println(Thread.currentThread() + " 子线程-退出,time->" + getTime());

            return "AlanChen";
        }));

        System.out.println(Thread.currentThread() + " 主线程-退出,time->" + getTime());
    }

    private static String getTime() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        return formatter.format(new Date());
    }
}

执行结果

Thread[main,5,main] 主线程-开始,time->2022-02-16 16:55:57 436
Thread[main,5,main] 主线程-退出,time->2022-02-16 16:55:57 472
子线程-是否为守护线程:false
Thread[pool-1-thread-1,5,main] 子线程-开始,time->2022-02-16 16:55:57 472
Thread[pool-1-thread-1,5,main] 子线程-退出,time->2022-02-16 16:55:59 476
1.2.2 FutureTask异步执行,取返回值

代码

public class FutureTaskTest2 {
    /**
     * FutureTask异步执行,取返回值
     */
    public static void main(String[] args) {
        System.out.println(Thread.currentThread() + " 主线程-开始,time->" + getTime());

        ExecutorService executor = Executors.newFixedThreadPool(3);

        FutureTask<String> futureTask = new FutureTask<>(() -> {
            System.out.println("子线程-是否为守护线程:" + Thread.currentThread().isDaemon());
            System.out.println(Thread.currentThread() + " 子线程-开始,time->" + getTime());

            Thread.sleep(2000);

            System.out.println(Thread.currentThread() + " 子线程-退出,time->" + getTime());

            return "AlanChen";
        });

        executor.submit(futureTask);

        // 中间可以先执行一堆逻辑,再阻塞获取返回结果
        System.out.println("中间可以先执行一堆逻辑,再阻塞获取返回结果");

        try {
            //阻塞获取返回结果
            System.out.println("异步执行返回结果:" + futureTask.get());
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread() + " 主线程-退出,time->" + getTime());
    }

    private static String getTime() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        return formatter.format(new Date());
    }
}

执行结果

Thread[main,5,main] 主线程-开始,time->2022-02-16 17:02:05 705
中间可以先执行一堆逻辑,再阻塞获取返回结果
子线程-是否为守护线程:false
Thread[pool-1-thread-1,5,main] 子线程-开始,time->2022-02-16 17:02:05 739
Thread[pool-1-thread-1,5,main] 子线程-退出,time->2022-02-16 17:02:07 750
异步执行返回结果:AlanChen
Thread[main,5,main] 主线程-退出,time->2022-02-16 17:02:07 751

二、CompletableFuture

在Java 8中,引入了CompletableFuture类。与Future接口一起,它还实现了CompletionStage接口。此接口定义了可与其他Future组合成异步计算契约。例如,CompletableFuture源码

public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
    // 省略相关源码
}
2.1 runAsync 和 supplyAsync方法

CompletableFuture 提供了四个静态方法来创建一个异步操作

CompletableFuture.runAsync(Runnable runnable);
CompletableFuture.runAsync(Runnable runnable, Executor executor);

CompletableFuture.supplyAsync(Supplier<U> supplier);
CompletableFuture.supplyAsync(Supplier<U> supplier, Executor executor);
  • runAsync 方法接收的是 Runnable 的实例,但是它没有返回值
  • supplyAsync 方法是JDK8函数式接口,无参数,会返回一个结果
  • 这两个方法是 executor 的升级,表示让任务在指定的线程池中执行,不指定的话,通常任务是在 ForkJoinPool.commonPool() 线程池中执行的。
2.2 runAsync

runAsync表示创建无返回值的异步任务,相当于ExecutorService submit(Runnable task)方法。

2.2.1 runAsync异步执行,取不到返回值-指定线程池

代码

public class RunAsyncTest {
    /**
     *  runAsync异步执行,取不到返回值-指定线程池
     */
    public static void main(String[] args) {
        System.out.println(Thread.currentThread() + " 主线程-开始,time->" + getTime());

        CompletableFuture.runAsync(() -> {
            System.out.println("子线程-是否为守护线程:" + Thread.currentThread().isDaemon());
            System.out.println(Thread.currentThread() + " 子线程-开始,time->" + getTime());

            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread() + " 子线程-退出,time->" + getTime());
        }, GlobalThreadPool.getExecutor());

        System.out.println(Thread.currentThread() + " 主线程-退出,time->" + getTime());
    }

    private static String getTime() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        return formatter.format(new Date());
    }
}

执行结果

Thread[main,5,main] 主线程-开始,time->2022-02-16 17:28:06 524
Thread[main,5,main] 主线程-退出,time->2022-02-16 17:28:06 577
子线程-是否为守护线程:false
Thread[pool-1-thread-1,5,main] 子线程-开始,time->2022-02-16 17:28:06 577
Thread[pool-1-thread-1,5,main] 子线程-退出,time->2022-02-16 17:28:08 580

Process finished with exit code 0

以上代码调用runAsync时,指定了线程池GlobalThreadPool.getExecutor(),通过执行结果我们可以看到,子线程不是守护线程,且执行结果正常。如果我们不指定线程池,用默认的线程池ForkJoinPool.commonPool(),会有什么不一样吗?

2.2.2 runAsync异步执行,取不到返回值-不指定线程池

代码

public class RunAsyncTest2 {
    /**
     *  runAsync异步执行,取不到返回值-不指定线程池
     */
    public static void main(String[] args) {
        System.out.println(Thread.currentThread() + " 主线程-开始,time->" + getTime());

        CompletableFuture.runAsync(() -> {
            System.out.println("子线程-是否为守护线程:" + Thread.currentThread().isDaemon());
            System.out.println(Thread.currentThread() + " 子线程-开始,time->" + getTime());

            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread() + " 子线程-退出,time->" + getTime());
        });

        System.out.println(Thread.currentThread() + " 主线程-退出,time->" + getTime());
    }

    private static String getTime() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        return formatter.format(new Date());
    }
}

执行结果

Thread[main,5,main] 主线程-开始,time->2022-02-16 17:37:03 196
Thread[main,5,main] 主线程-退出,time->2022-02-16 17:37:03 229
子线程-是否为守护线程:true
Thread[ForkJoinPool.commonPool-worker-1,5,main] 子线程-开始,time->2022-02-16 17:37:03 229

Process finished with exit code 0

通过执行结果,我们可以看到,子线程为守护线程,且子线程休眠2秒后,子线程-退出的打印语句没有被执行,即runAsync()方法里的逻辑代码没有被完全执行,如果这段代码在项目中使用,就会出现重大BUG。

出现这个问题的原因在于子线程为守护线程,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。

2.2.3 守护线程(Daemon Thread)

在Java中有两类线程:

  • 用户线程 (User Thread)
  • 守护线程 (Daemon Thread)。

所谓守护 线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。

用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。

Java程序入口就是由JVM启动main线程,main线程又可以启动其他线程。当所有线程都运行结束时,JVM退出,进程结束。

将线程转换为守护线程可以通过调用Thread对象的setDaemon(true)方法来实现。在使用守护线程时需要注意一下几点:

  • thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。

  • 在Daemon线程中产生的新线程也是Daemon的。

  • 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。

2.3 supplyAsync

supplyAsync表示创建带返回值的异步任务的,相当于ExecutorService submit(Callable<T> task)方法。

2.3.1 supplyAsync异步执行,不取返回值-指定线程池

代码

public class SupplyAsyncTest {
    /**
     * supplyAsync异步执行,不取返回值-指定线程池
     */
    public static void main(String[] args) {
        System.out.println(Thread.currentThread() + " 主线程-开始,time->" + getTime());

        CompletableFuture.supplyAsync(() -> {
            System.out.println("子线程-是否为守护线程:" + Thread.currentThread().isDaemon());
            System.out.println(Thread.currentThread() + " 子线程-开始,time->" + getTime());

            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread() + " 子线程-退出,time->" + getTime());

            return "AlanChen";
        }, GlobalThreadPool.getExecutor());

        System.out.println(Thread.currentThread() + " 主线程-退出,time->" + getTime());
    }

    private static String getTime() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        return formatter.format(new Date());
    }
}

执行结果

Thread[main,5,main] 主线程-开始,time->2022-02-16 17:58:52 557
Thread[main,5,main] 主线程-退出,time->2022-02-16 17:58:52 594
子线程-是否为守护线程:false
Thread[pool-1-thread-1,5,main] 子线程-开始,time->2022-02-16 17:58:52 595
Thread[pool-1-thread-1,5,main] 子线程-退出,time->2022-02-16 17:58:54 600

Process finished with exit code 0

supplyAsync()方法和runAsync()方法一样有守护线程的问题,因此我们调用时指定线程池,而不用默认的线程池。

2.3.2 supplyAsync异步执行,同步取返回值-get

代码

public class SupplyAsyncTest2 {
    /**
     *  supplyAsync异步执行,同步取返回值
     */
    public static void main(String[] args) {
        System.out.println(Thread.currentThread() + " 主线程-开始,time->" + getTime());

        CompletableFuture<String> completableFuture =CompletableFuture.supplyAsync(() -> {
            System.out.println("子线程-是否为守护线程:" + Thread.currentThread().isDaemon());
            System.out.println(Thread.currentThread() + " 子线程-开始,time->" + getTime());

            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread() + " 子线程-退出,time->" + getTime());

            return "AlanChen";
        }, GlobalThreadPool.getExecutor());

        // 中间可以先执行一堆逻辑,再阻塞获取返回结果
        System.out.println("中间可以先执行一堆逻辑,再阻塞获取返回结果");

        try {
            //阻塞获取返回结果
            System.out.println("异步执行返回结果:" + completableFuture.get());
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread() + " 主线程-退出,time->" + getTime());
    }

    private static String getTime() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        return formatter.format(new Date());
    }
}

执行结果

Thread[main,5,main] 主线程-开始,time->2022-02-16 18:02:45 956
中间可以先执行一堆逻辑,再阻塞获取返回结果
子线程-是否为守护线程:false
Thread[pool-1-thread-1,5,main] 子线程-开始,time->2022-02-16 18:02:46 000
Thread[pool-1-thread-1,5,main] 子线程-退出,time->2022-02-16 18:02:48 008
异步执行返回结果:AlanChen
Thread[main,5,main] 主线程-退出,time->2022-02-16 18:02:48 009
2.3.3 supplyAsync异步执行,异步取返回值-whenCompleteAsync

代码

public class SupplyAsyncTest3 {
    /**
     * supplyAsync异步执行,异步取返回值
     */
    public static void main(String[] args) {
        System.out.println(Thread.currentThread() + " 主线程-开始,time->" + getTime());

        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("子线程-是否为守护线程:" + Thread.currentThread().isDaemon());
            System.out.println(Thread.currentThread() + " 子线程-开始,time->" + getTime());

            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }

            //int a=1/0;

            System.out.println(Thread.currentThread() + " 子线程-退出,time->" + getTime());

            return "AlanChen";
        }, GlobalThreadPool.getExecutor());

        // 中间可以执行一堆逻辑
        System.out.println("中间可以执行一堆逻辑");

        //异步回调获取返回值
        completableFuture.whenCompleteAsync((result, e) -> {
            System.out.println("-------异步执行返回结果:" + result);
            System.out.println("-------e=" + e);
        }).exceptionally(f -> {
            System.out.println("-----exception:" + f.getMessage());
            return "出现异常后的返回值";
        });

        System.out.println(Thread.currentThread() + " 主线程-退出,time->" + getTime());
    }

    private static String getTime() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        return formatter.format(new Date());
    }
}

执行结果

Thread[main,5,main] 主线程-开始,time->2022-02-16 18:12:45 109
中间可以执行一堆逻辑
子线程-是否为守护线程:false
Thread[pool-1-thread-1,5,main] 子线程-开始,time->2022-02-16 18:12:45 149
Thread[main,5,main] 主线程-退出,time->2022-02-16 18:12:45 150
Thread[pool-1-thread-1,5,main] 子线程-退出,time->2022-02-16 18:12:47 159
-------异步执行返回结果:AlanChen
-------e=null
2.4 supplyAsync-异步回调thenApply / thenApplyAsync

thenApply 表示某个任务执行完成后执行的动作,即回调方法,会将该任务的执行结果即方法返回值作为入参传递到回调方法中。

2.4.1 thenApply同步调用

代码

public class SupplyAsyncTest5 {

    public static void main(String[] args) {
        System.out.println(Thread.currentThread() + " 主线程-开始,time->" + getTime());

        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("子线程1-是否为守护线程:" + Thread.currentThread().isDaemon());
            System.out.println(Thread.currentThread() + " 子线程1-开始,time->" + getTime());

            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread() + " 子线程1-退出,time->" + getTime());

            return "AlanChen";
        }, GlobalThreadPool.getExecutor());

        try {
            Thread.sleep(3000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 中间可以执行一堆逻辑
        System.out.println("中间可以执行一堆逻辑");

        //同步执行
        CompletableFuture<String> completableFuture2=completableFuture.thenApply((result)->{
            System.out.println("子线程2-是否为守护线程:" + Thread.currentThread().isDaemon());
            System.out.println(Thread.currentThread() + " 子线程2-开始,time->" + getTime());

            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread() + " 子线程2-退出,time->" + getTime());

            return "Hello "+result;
        });

        //异步回调获取返回值
        completableFuture2.whenCompleteAsync((result, e) -> {
            System.out.println("-------异步执行返回结果:" + result);
            System.out.println("-------e=" + e);
        });

        System.out.println(Thread.currentThread() + " 主线程-退出,time->" + getTime());
    }

    private static String getTime() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        return formatter.format(new Date());
    }
}

执行结果

Thread[main,5,main] 主线程-开始,time->2022-02-16 18:31:07 833
子线程1-是否为守护线程:false
Thread[pool-1-thread-1,5,main] 子线程1-开始,time->2022-02-16 18:31:07 874
Thread[pool-1-thread-1,5,main] 子线程1-退出,time->2022-02-16 18:31:09 877
中间可以执行一堆逻辑
子线程2-是否为守护线程:false
Thread[main,5,main] 子线程2-开始,time->2022-02-16 18:31:10 878
Thread[main,5,main] 子线程2-退出,time->2022-02-16 18:31:12 892
Thread[main,5,main] 主线程-退出,time->2022-02-16 18:31:12 893
-------异步执行返回结果:Hello AlanChen
-------e=null

Process finished with exit code 0
2.4.2 thenApplyAsync异步调用

代码

public class SupplyAsyncTest6 {

    public static void main(String[] args) {
        System.out.println(Thread.currentThread() + " 主线程-开始,time->" + getTime());

        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("子线程1-是否为守护线程:" + Thread.currentThread().isDaemon());
            System.out.println(Thread.currentThread() + " 子线程1-开始,time->" + getTime());

            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread() + " 子线程1-退出,time->" + getTime());

            return "AlanChen";
        }, GlobalThreadPool.getExecutor());

        try {
            Thread.sleep(3000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 中间可以执行一堆逻辑
        System.out.println("中间可以执行一堆逻辑");

        //异步执行
        CompletableFuture<String> completableFuture2 = completableFuture.thenApplyAsync((result) -> {
            System.out.println("子线程2-是否为守护线程:" + Thread.currentThread().isDaemon());
            System.out.println(Thread.currentThread() + " 子线程2-开始,time->" + getTime());

            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread() + " 子线程2-退出,time->" + getTime());

            return "Hello " + result;
        }, GlobalThreadPool.getExecutor());

        //异步回调获取返回值
        completableFuture2.whenCompleteAsync((result, e) -> {
            System.out.println("-------异步执行返回结果:" + result);
            System.out.println("-------e=" + e);
        });

        System.out.println(Thread.currentThread() + " 主线程-退出,time->" + getTime());
    }

    private static String getTime() {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        return formatter.format(new Date());
    }
}

执行结果

Thread[main,5,main] 主线程-开始,time->2022-02-16 18:31:10 701
子线程1-是否为守护线程:false
Thread[pool-1-thread-1,5,main] 子线程1-开始,time->2022-02-16 18:31:10 741
Thread[pool-1-thread-1,5,main] 子线程1-退出,time->2022-02-16 18:31:12 752
中间可以执行一堆逻辑
子线程2-是否为守护线程:false
Thread[pool-1-thread-1,5,main] 子线程2-开始,time->2022-02-16 18:31:13 748
Thread[main,5,main] 主线程-退出,time->2022-02-16 18:31:13 749
Thread[pool-1-thread-1,5,main] 子线程2-退出,time->2022-02-16 18:31:15 752
-------异步执行返回结果:Hello AlanChen
-------e=null

Process finished with exit code 0

相关文章

网友评论

      本文标题:Java8之CompletableFuture

      本文链接:https://www.haomeiwen.com/subject/txpdlrtx.html