多线程-异步编排


大家知道Java语言原生支持多线程,实现多线程的方式主要有以下四种:

  1. 继承 Thread 类并重写 run方法创建线程,实现简单但不可以继承其他类;
  2. 实现 Runnable 接口并重写 run 方法,避免了单继承局限性,编程更加灵活,实现解耦;
  3. 实现 Callable 接口并重写 call 方法,创建线程。可以获取线程执行结果的返回值,并且可以抛出异常;
  4. 使用线程池创建。

区别:

  • 方式1和方式2:主进程无法获取线程的运算结果。
  • 方式3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可能导致服务器资源耗尽。
  • 方式4:可以控制资源,系统性能稳定

先来写个Demo吧:

public class ThreadTest {
    // 当前系统池只有一两个,每个异步任务直接提交给线程池,让他自己去执行
    public static ExecutorService service = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start....");
        /**
         * 1. 继承 Thread
         *      Thread01 thread = new Thread01();
         *      thread.start();
         * 2. 实现 Runnable 接口
         *      Runable01 runable01 = new Runable01();
         *      new Thread(runable01).start();
         * 3. 实现 Callable接口 + FutureTask (可以拿到返回结果,可以处理异常)
         *      FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
         *      new Thread(futureTask).start();
         *      // 阻塞等待整个线程执行完成,获取返回结果
         *      Integer integer = futureTask.get();
         * 4. 线程池
         *      给线程池直接提交任务.
         *      我们以后在业务代码里面,以上三种启动线程的方式都不用。【将所有的多线程异步任务都交给线程池执行】
         * 区别:
         *      1、2不能得到返回值,3可以获取返回值
         *      1、2、3都不能控制资源
         *      4 可以控制资源,系统性能稳定
         */
        service.execute(new Runable01());
    }

    public static class Thread01 extends Thread {
        @Override
        public void run() {
            System.out.println("当前线程: " + Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:" + i);
        }
    }

    public static class Runable01 implements Runnable {
        @Override
        public void run() {
            System.out.println("当前线程: " + Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:" + i);
        }
    }

    public static class Callable01 implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            System.out.println("当前线程: " + Thread.currentThread().getId());
            int i = 10/2;
            System.out.println("运行结果:" + i);
            return i;
        }
    }
}

前两种实现方法这里不做过多讲解。

1、Callable接口

实现 Callable 接口并重写 call 方法,创建线程。可以获取线程执行结果的返回值,并且可以抛出异常;

  • 优点:
    • 可以获得任务执行返回值;
    • 通过与Future的结合,可以实现利用Future来跟踪异步计算的结果。

Runbable 和 Callable 的区别?

  1. Callable规定的方法是call(),Runnable规定的方法是run().
  2. Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
  3. call方法可以抛出异常,run方法不可以
  4. 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
    在这里插入图片描述

Future接口

  • Future是一个接口,代表了一个异步计算的结果。接口中的方法用来检查计算是否完成、等待完成和得到计算的结果。
  • 当计算完成后,只能通过get()方法得到结果,get方法会阻塞直到结果准备好了。
  • 如果想取消,那么调用cancel()方法。其他方法用于确定任务是正常完成还是取消了。
  • 一旦计算完成了,那么这个计算就不能被取消。

FutureTask类

  • FutureTask类实现了RunnableFuture接口,而RunnnableFuture接口继承了Runnable和Future接口,所以说FutureTask是一个提供异步计算的结果的任务。
  • FutureTask可以用来包装Callable或者Runnbale对象。因为FutureTask实现了Runnable接口,所以FutureTask也可以被提交给Executor。

Callable的执行方式

	// FutureTask
	FutureTask<Integer> futureTask1 = new FutureTask<>(new MyThread2());
	new Thread(futureTask1,"BB").start();
  	System.out.println(futureTask1.get());

当然也可以进行Lambda进行简化(Callable是函数式接口):

// Lambda表达式进行简化
FutureTask<Integer> futureTask2 = new FutureTask<>(()->{
    return 1024;
});

2、线程池

线程池[ExecutorService]
原生:service.execute(new Runable01());
创建

  1. Executors
  2. new ThreadPoolExecutor()

为什么需要线程池?

  1. 避免线程在创建销毁时造成的消耗
  2. 提高系统的响应速度
  3. 根据系统的承受能力调整线程数量的大小等

线程池的分类

  1. newCachedThreadPool :创建一个可进行缓存重复利用的线程池
  2. newFixedThreadPool :创建一个可重用固定线程数的线程池
  3. newSingleThreadExecutor :创建一个使用单个worker线程的Executor,以无届队列方式来运行该线程。线程池中最多执行一个线程,之后提交的线程将会排在队列中以此执行。
  4. newSingleThreadScheduledExecutor :创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期执行
  5. newScheduledThreadPool :创建一个线程池,它可安排在给定延迟后运行命令或者定期的执行
  6. newWorkStealingPool :创建一个带并行级别的线程池,并行级别决定了同一时刻最多有多少个线程在执行,如果不传并行级别参数,将默认为当前系统的 CPU 个数。

核心参数

  • corePoolSize核心线程数,[一直存在,除非设置(allowCoreThreadTimeOut)],创建好以后就准备的线程数量,就等待来接受异步任务去执行
  • maximumPoolSize最大线程数量;控制资源
  • keepAliveTime存活时间,如果当前线程数量大于核心线程数,只要线程空闲大于指定的时间(keepAliveTime),就会释放空闲的核心线程外的线程(maximumPoolSize - corePoolSize)。
  • unit时间单位,为 keepAliveTime 指定时间单位
  • BlockingWorkQueue阻塞队列;如果任务很多,线程都在工作,将目前多的任务放在队列里面。只要有线程空闲,就会去队列里面取出新的任务继续执行
  • ThreadFactory创建线程的工厂类
  • Handler:拒绝策略,如果队列满了,按照我们指定的拒绝策略,拒绝执行任务
    • AbortPolicy (终止策略):,丢弃任务,并抛出异常。(jdk默认策略)
    • DiscardPolicy丢弃策略):丢弃任务,不抛出异常
    • DiscardOldestPolicy弃老策略):丢弃队列最前面的任务,然后重新执行任务
    • CallerRunsPolicy调用方策略): 既不丢弃任务也不抛出异常,而是将某些任务回退到调用者,让调用者去执行它

线程池的工作过程?

  1. 线程池创建,准备好 corePoolSize(核心线程数量)的线程,准备接收任务。
  2. 新的任务进来,用core准备好的空闲线程执行
  3. 若核心线程满了,就将再进来的任务放入阻塞队列中。空闲的core就会自己去阻塞队列获取任务执行。
  4. 阻塞队列满了,就直接开新线程执行,最大只能开到maximumPoolSize最大核心线程数指定的数量
  5. 若最大线程数量满了,就交给拒绝策略去处理。
  6. 若有空闲线程在指定的时间 KeepAliveTime 以后,会释放空闲的线程(这里指的是核心线程以外的线程)。

2.1、配置线程池

第一步、编写创建线程池工具类

1、添加线程池属性配置类,并注入到容器中

package com.atguigu.gulimall.product.config;

@ConfigurationProperties(prefix = "gulimall.thread")
@Component
@Data
public class ThreadPoolConfigProperties {
    private Integer coreSize;
    private Integer maxSize;
    private Integer keepAliveTime;
}

这里提示需要导入一个工具依赖,不导也可以:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

2、添加线程池属性的配置

在gulimall-product服务中加入以下配置:

# 配置线程池
gulimall:
  thread:
    core-size: 20
    max-size: 200
    keep-alive-time: 10

3、线程池配置,获取线程池的属性值这里直接调用与配置文件相对应的属性配置类

package com.atguigu.gulimall.product.config;

@Configuration
public class MyThreadConfig {

    @Bean
    public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool) {
        return new ThreadPoolExecutor(pool.getCoreSize(),
                pool.getMaxSize(),
                pool.getKeepAliveTime(),
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
    }
}

使用的时候只需要通过:

@Autowired
ThreadPoolExecutor executor;

2.2、CompletableFuture 组合式异步编排

Future 接口

Future 接口(FutureTask实现类)定义了操作 异步任务执行一些方法,如获取异步任务的执行结果、取消任务的执行、判断任务是否被取消、判断任务执行是否完毕等。

缺点:

  • get() 阻塞:一旦调用 get() 方法求结果,如果计算没有完成容易导致程序阻塞。
  • isDone() 轮询:轮训的方式会耗费无谓的CPU资源,而且也不见得能及时地得到计算结果。

CompletableFuture 对 Future 的改进

get()方法在Future 计算完成之前会一直处在 阻塞状态 下,isDone()方法容易耗费CPU资源,对于真正的异步处理我们希望是可以通过传入回调函数,在Future结束时自动调用该回调函数,这样,我们就不用等待结果。

阻塞的方式和异步编排的设计理念相违背,而轮训的方式会耗费无谓的CPU资源。因此,JDK8设计出 CompleatbleFuture。

CompleatbleFuture 提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方。

public class CompletableFuture<T> implements Future<T>, CompletionStage<T> 

业务场景:
查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必然需要话费更多的时间:

  1. 获取sku的基本信息 0.5s
  2. 获取sku的图片信息 0.5s
  3. 获取sku的促销信息 1s
  4. 获取spu的所有销售属性 1s
  5. 获取规格参数组及组下的规格参数 1.5s
  6. spu详情 1s

假如商品详情页的每个查询,需要如下标注的时间才能完成。那么,用户需要5.5s后才能看到商品详情页的内容。很显然是不能接受的。如果有多个线程同时完成这6步操作,也许只需要1.5s即可完成响应

2.2.1、创建异步对象

创建异步对象

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

  • runAsync方法不支持返回值。
  • supplyAsync可以支持返回值。
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。以下所有的方法都类同。

public static void main(String[] args) throws ExecutionException, InterruptedException {
    System.out.println("main...start....");
  /**
   * 方法完成之后的感知
   */
  CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
    System.out.println("当前线程: " + Thread.currentThread().getId());
    int i = 10 / 0;
    System.out.println("运行结果:" + i);
    return i;
  }, executor).whenComplete((result,exception)->{
    // 虽然能得到异常信息,但是没法修改返回数据
    System.out.println("异步任务成功完成了...结果是:" + result+";异常是:"+exception);
  }).exceptionally(throwable -> {
    // 可以感知异常同时返回指定默认值
    System.out.println(throwable);
    return 10;
  });
    System.out.println(future2.get());

    System.out.println("main...end....");
}

2.2.2、计算完成时回调方法

计算完成时回调方法

当CompletableFuture的计算结果完成,或者抛出异常的时候,可以执行特定的Action。主要是下面的方法:

//可以处理异常,无返回值
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
//可以处理异常,有返回值
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)

whenComplete 可以处理正常和异常的计算结果【感知】,exceptionally 处理异常情况【修改】
whenComplete 和 whenCompleteAsync 的区别:

  • whenComplete :是执行当前任务的线程继续执行 whenComplete 的任务
  • whenCompleteAsync : 是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行

方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main...start....");
/*
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            System.out.println("当前线程: " + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
        }, executor);*/

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程: " + Thread.currentThread().getId());
            int i = 10 / 0;
            System.out.println("运行结果:" + i);
            return i;
        }, executor).whenComplete((result,exception)->{
            // 虽然能得到异常信息,但是没法修改返回数据
            System.out.println("异步任务成功完成了...结果是:" + result+";异常是:"+exception);
        }).exceptionally(throwable -> {
            // 可以感知异常同时返回指定默认值
            return 10;
        });
        System.out.println(future2.get());
        
        System.out.println("main...end....");
    }

2.2.3、handle 方法

handle 方法

handle 是执行任务完成时对结果的处理。 handle 方法和 thenApply 方法处理方式基本一样。不同的是:

  • handle 是在任务完成后再执行,还可以处理异常的任务。
  • thenApply 只可以执行正常的任务,任务出现异常则不执行 thenApply 方法。
public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn,Execut
/**
 * 方法执行完成后的处理
 */
CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> {
    System.out.println("当前线程: " + Thread.currentThread().getId());
    int i = 10 / 2;
    System.out.println("运行结果:" + i);
    return i;
}, executor).handle((res,thr)->{
    if (res != null) {
        return res *2;
    }
    if (thr != null) {
        return 0;
    }
    return 0;
});
System.out.println(future3.get());

在这里插入图片描述


2.2.4、线程串行化

线程串行化

  • thenRun 方法:
    只要上面的任务执行完成,就开始执行 thenRun,只要处理完任务后,执行 thenRun 的后续操作
  • thenAccept 方法:
    消费处理结果。接收任务的处理结果,并消费处理,无返回结果。
  • thenApply 方法:
    当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值。

带有 Async 默认是异步执行的。同之前

  • 不带 Asycn :共同同一个线程
  • 带 Asycn : 交给线程池来进行执行
// thenRun
public CompletableFuture<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,
                                              Executor executor);

// thenAccept
public CompletableFuture<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,
                                                 Executor executor);

  
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync
        (Function<? super T,? extends U> fn);
public <U> CompletionStage<U> thenApplyAsync
        (Function<? super T,? extends U> fn,
         Executor executor);

测试代码:

/**
 * 线程串行化
 *  1)、thrnRun :不能获取到上一步的执行结果,并无返回值
 *  2)、thenAccept : 能接收上一步结果,但是无返回值
 *  3)、thenApply :能接收上一步结果,并有返回值
 */
CompletableFuture<Void> future1 = CompletableFuture.supplyAsync(() -> {
    System.out.println("当前线程1: " + Thread.currentThread().getId());
    int i = 10 / 2;
    System.out.println("运行结果:" + i);
    return i;
}, executor).thenRunAsync(() -> {
    System.out.println("任务2启动了...");
}, executor);

CompletableFuture<Void> future2 = CompletableFuture.supplyAsync(() -> {
    System.out.println("当前线程2: " + Thread.currentThread().getId());
    int i = 10 / 2;
    System.out.println("运行结果:" + i);
    return i;
}, executor).thenAcceptAsync(res->{
    System.out.println("任务2启动了..." + res);
},executor);

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    System.out.println("当前线程2: " + Thread.currentThread().getId());
    int i = 10 / 2;
    System.out.println("运行结果:" + i);
    return i;
}, executor).thenApplyAsync(res -> {
    System.out.println("任务2启动了..." + res);
    return "Hello" + res;
}, executor);
System.out.println("返回值:"+future.get());

2.2.5、两任务组合-都要完成

两任务组合-都要完成

thenCombine 会把两个 CompletionStage 的任务都执行完成后,把两个任务的结果一块交给 thenCombine 来处理。

  • runAfterBoth
    组合两个 future,不需要获取 future 的结果,只需两个 future 处理完任务后,处理该任务。

    两个CompletionStage,都完成了计算才会执行下一步的操作(Runnable)

    public CompletionStage<Void> runAfterBoth(CompletionStage<?> other,Runnable action);
    public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action);
    public CompletionStage<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor 
    
  • thenAcceptBoth
    组合两个 future,获取两个 future 任务的返回结果,然后处理任务,没有返回值
    当两个CompletionStage都执行完成后,把结果一块交给thenAcceptBoth来进行消耗

    public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);
    public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);
    public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action,     Executor executor);
    
  • thenCombine
    组合两个 future,获取两个 future 的返回结果,并返回当前任务的返回值
    thenCombine 会把 两个 CompletionStage 的任务都执行完成后,把两个任务的结果一块交给 thenCombine 来处理。

    public <U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
    public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
    public <U,V> CompletionStage<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn,Executor executor);
    

测试代码:

/**
 * 两个异步任务都完成
 */
CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
    System.out.println("任务1线程: " + Thread.currentThread().getId());
    int i = 10 / 4;
    System.out.println("任务1结束");
    return i;
}, executor);
CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
    System.out.println("任务2线程: " + Thread.currentThread().getId());
    System.out.println("任务2结束");
    return "Hello";
}, executor);

future01.runAfterBothAsync(future02, ()->{
    System.out.println("任务3开始...");
},executor);

future01.thenAcceptBothAsync(future02, (f1,f2)->{
    System.out.println("任务3开始,之前的结果:f1="+f1+";f2="+f2);
},executor);

CompletableFuture<String> future = future01.thenCombineAsync(future02, (f1, f2) -> {
    return f1 + ":" +f2 +"->HaHa";
}, executor);
System.out.println("方法3的返回结果:" + future.get());

2.2.6、两任务组合-只要有一个任务完成就执行第三个

两任务组合-只要有一个任务完成就执行第三个

两个 CompletionStage 的返回类型要一致

  • runAfterEither 方法
    两个任务有一个任务完成,不需要获取 future 的结果,处理任务,也没有返回值
    两个CompletionStage,任何一个完成了都会执行下一步的操作(Runnable)

    public CompletionStage<Void> runAfterEither(CompletionStage<?> other,Runnable action);
    public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action);
    public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor);
    
  • acceptEither 方法
    两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值
    两个CompletionStage,谁执行返回的结果快,我就用那个CompletionStage的结果进行下一步的消耗操作。

    public CompletionStage<Void> acceptEither(CompletionStage<? extends T> other,Consumer<? super T> action);
    public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action);
    public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? supe
    
  • applyToEither 方法
    两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值
    两个CompletionStage,谁执行返回的结果快,我就用那个CompletionStage的结果进行下一步的转化操作。

    public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other,Function<? super T, U> fn);
    public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn);
    public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? sup
    

测试代码:

        /**
         * 两个异步任务只要有一个完成,我们就执行任务3
         *  runAfterEither: 不感知结果,自己也没有返回值
         *  acceptEither : 感知结果,自己没有返回值
         */
        CompletableFuture<Object> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程: " + Thread.currentThread().getId());
            int i = 10 / 4;
            System.out.println("任务1结束");
            return i;
        }, executor);
        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程: " + Thread.currentThread().getId());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务2结束");
            return "Hello";
        }, executor);

//        CompletableFuture<Void> future3 = future01.runAfterEitherAsync(future02, () -> {
//            System.out.println("任务3开始...");
//        }, executor);
//        CompletableFuture<Void> future4 = future01.acceptEitherAsync(future02, (res) -> {
//            System.out.println("任务3开始..." + res);
//        }, executor);
        CompletableFuture<String> future = future01.applyToEitherAsync(future02, res -> {
            System.out.println("任务3开始..." + res);
            return res.toString() + "-->haha";
        }, executor);

        System.out.println("main...end...." + future.get());
    }

在这里插入图片描述

2.2.7、多任务组合

  • allOf : 等待所有任务完成
  • anyOf :只有一个任务完成
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);

allOf 测试代码:

CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
    System.out.println("查询商品图片信息");
    return "hllo.jpg";
}, executor);
CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
    System.out.println("查询商品的属性");
    return "星空白+256G";
}, executor);
CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("查询商品的介绍");
    return "苹果";
}, executor);
CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttr, futureDesc);
allOf.get();    // 等待所有结果完成

System.out.println("main...end...." );

在这里插入图片描述

anyOf 测试代码:

CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("查询商品图片信息");
    return "hllo.jpg";
}, executor);
CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("查询商品的属性");
    return "星空白+256G";
}, executor);
CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("查询商品的介绍");
    return "苹果";
}, executor);
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureDesc);
anyOf.get();

在这里插入图片描述

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值