异步&线程池

一、线程回顾

1、初始化线程的 4 种方式

1)、继承 Thread

2)、实现 Runnable 接口

3)、实现 Callable 接口 + FutureTask (可以拿到返回结果,可以处理异常)

4)、线程池

方式 1 和方式 2:主进程无法获取线程的运算结果。不适合当前场景

方式 3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致服务器资源耗尽。

方式 4:通过如下两种方式初始化线程池

public static void main(String[] args) {
        System.out.println("main-*--------start-*-------");
        Thread01 thread01 = new Thread01();
        thread01.start();
        System.out.println("main-*--------end-*-------");
}

public static class Thread01 extends Thread {
    @Override
    public void run() {
        System.out.println("当前线程号Thread01:"+Thread.currentThread().getId());
    }
}
public static void main(String[] args) {
    System.out.println("main-*--------start-*-------");
    Runnable01 runnable01 = new Runnable01();
    new Thread(runnable01).start();
    System.out.println("main-*--------end-*-------");
}


public static class Runnable01 implements Runnable {
    @Override
    public void run() {
        System.out.println("当前线程号Runnable01:"+Thread.currentThread().getId());
    }
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
    System.out.println("main-*--------start-*-------");
    // 阻塞等待整个线程执行完成,获取返回结果
    FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
    new Thread(futureTask).start();
    //阻塞等待线程执行完成返回结果
    Integer integer = futureTask.get();
    System.out.println("main-*--------end-*-------"+integer);
}
public static class Callable01 implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("当前线程号Callable01:"+Thread.currentThread().getId());
        return 100/2;
    }
}

线程池

我们以后在实际业务中前三种启动线程的方式都不用,我们将所有的多线程异步任务都交给线程池执行。

线程池(ThreadPoolExecutor)的继承机构:

顶级接口Executor: 定义了execute(Runnbale command)方法

​ 子接口ExecutorService

​ 最常用实现类:ThreadPoolExecutor

如果调用ThreadPoolExecutor实现类的构造器创建线程池时并不会马上创建线程,只有当调用其execute方法时才会去创建线程,类似于延时加载(创建线程)。

使用线程池的好处:

  • 降低资源的消耗:通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
  • 提高响应速度 :因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无须创建新的线程就能执行
  • 提高线程的可管理性 :线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配

创建线程池进行异步处理

Executors.newFiexedThreadPool(3); //或者 new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit unit, workQueue, threadFactory, handler);

通过线程池性能稳定,也可以获取执行结果,并捕获异常。但是,在业务复杂情况下,一个异步调用可能会依赖于另一个异步调用的执行结果。

2、线程池的七大参数

* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
池中一直保持的线程的数量,即使线程空闲。除非设置了 allowCoreThreadTimeOut
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
池中允许的最大的线程数
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating. 
当线程数大于核心线程数的时候,线程在最大多长时间没有接到新任务就会终止释放,最终线程池维持在 corePoolSize 大小
* @param unit the time unit for the {@code keepAliveTime} argument
时间单位
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method. 
阻塞队列,用来存储等待执行的任务,如果当前对线程的需求超过了corePoolSize大小,就会放在这里等待空闲线程执行。
* @param threadFactory the factory to use when the executor
* creates a new thread
创建线程的工厂,比如指定线程名等
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
拒绝策略,如果线程满了,线程池就会使用拒绝策略。

  1. corePoolSize: 核心线程数 【一直存在,除非设置核心线程的超时时间allowCoreThreadTimeOut】;核心线程数的意思就是线程池创建好以后就准备就绪的线程数量,核心线程如果是空闲的就会等待接收异步任务去执行。核心线程数是多少,线程工厂就会创建几个线程并且start。
  2. maximumPoolSize: 最大线程数量,用于控制线程资源
  3. keepAliveTime: 存活时间。如果当前正在运行的线程数量大于core数量,那么就会释放空闲的线程(释放数量=maximumPoolSize-corePoolSize)。释放条件:只要线程空闲时间大于指定的keepAliveTime;
  4. unit: 线程空闲时间的时间单位
  5. BlockingQueue workQueue: 工作队列。如果任务有很多,就会将多的任务放在队列里面。只要有线程空闲,就会去队列里面取出新的任务继续执行
  6. ThreadFactory: 创建线程的工厂
  7. RejectedExecutionHandler handler: 拒绝策略:如果队列满了,就会按照我们指定的拒绝策略,拒绝执行新的任务。
    • AbortPolicy :抛出运行时异常RejectedExecutionException。这种策略丢弃任务,并抛出异常。(jdk默认策略)
    • CallerRunsPolicy : 线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。
    • DiscardOldestPolicy(弃老策略)jdk:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序。
    • DiscardPolicy :发生拒绝策略时,不触发任何动作

运行流程:

1、线程池创建,准备好 core 数量的核心线程,准备接受任务

2、新的任务进来,用 core 准备好的空闲线程执行。

(1) 、core 满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队列获取任务执行

(2) 、阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量

(3) 、max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁。最终保持到 core 大小

(4) 、如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策略进行处理

3、所有的线程创建都是由指定的 factory 创建的。

面试:

一个线程池 core 7; max 20 ,queue:50,100 并发进来怎么分配的;

先有 7 个能直接得到执行,接下来 50 个进入队列排队,在多开 13 个继续执行。现在 70 个被安排上了。剩下 30 个默认拒绝策略。

3、常见的 4 种线程池

  • newCachedThreadPool
    • 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • newFixedThreadPool
    • 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  • newScheduledThreadPool
    • 创建一个定长线程池,支持定时及周期性任务执行。
  • newSingleThreadExecutor
    • 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

4、开发中为什么使用线程池

  • 降低资源的消耗
    • 通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
  • 提高响应速度
    • 因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
  • 提高线程的可管理性
    • 线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配。

CompletableFuture组合式异步编程

注意事项:使用异步编排CompletableFuture必须调用它的affof()方法,否则可能出现线程池执行过程中线程被挂起的情况,也就会导致任务执行阻塞。

在我们的项目中,查询商品详情页的逻辑比较复杂,有些数据甚至涉及到了远程调用(极其花费时间),具体查询数据流程如下:

  1. 获取sku的基本信息 0.5s
  2. 获取sku的图片信息 0.5s
  3. 获取sku的促销信息 1s
  4. 获取spu的所有销售属性 1s
  5. 获取规格参数组及组下的规格参数 1.5s
  6. spu详情 1s(以上所有时间只是用于描述问题)

这里如果是单线程同步执行的话,访问我们的商品详情页需要足足5.5s。但是多线程下可能只需要1.5s即可(即执行时间最长的那个任务所需时间)。但我们这里的业务又有所不同: 1、2、3可以异步完成,4、5、6依赖于1完成之后的结果,4、5、6又可以异步完成,所以我们需要使用CompletableFuture进行异步编排 ,以使得456任务在获取到1执行完成之后的结果之后才异步执行。

如果有多个线程同时完成这 6 步操作,也许只需要 1.5s 即可完成响应。

Future 是 Java 5 添加的类,用来描述一个异步计算的结果。你可以使用`isDone`方法检查计算是否完成,或者使用`get`阻塞住调用线程,直到计算完成返回结果,你也可以使用`cancel` 方法停止任务的执行。

虽然`Future`以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的 CPU 资源,而且也不能及时地得到计算结果,为什么不能用观察者设计模式当计算结果完成及时通知监听者呢?

很多语言,比如 Node.js,采用回调的方式实现异步编程。Java 的一些框架,比如 Netty,自己扩展了 Java 的 `Future`接口,提供了`addListener`等多个扩展方法;Google guava 也提供了通用的扩展 Future;Scala 也提供了简单易用且功能强大的 Future/Promise 异步编程模式。

作为正统的 Java 类库,是不是应该做点什么,加强一下自身库的功能呢?

在 Java 8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture,提供了非常强大的Future 的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合 CompletableFuture 的方法。

CompletableFuture 类实现了 Future 接口,所以你还是可以像以前一样通过`get`方法阻塞或者轮询的方式获得结果,但是这种方式不推荐使用。

CompletableFuture 和 FutureTask 同属于 Future 接口的实现类,都可以获取线程的执行结果。

1、创建异步对象

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

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() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。以下所有的方法都类同。

  • runAsync方法不支持返回值。
  • supplyAsync可以支持返回值,使用fet获取返回结果

为什么一个支持返回值一个不支持呢?看下其参数,一个Runnable接口一个Supplier接口,这两个一个是直接run方法运行,一个是get方法会返回一个值。后文的接收参数无返回值用的Consumer接口,accept方法只接收参数不返回参数。但这个返回值只是给CompletableFuture这个类的泛型赋值,泛型会决定方法究竟有无具体有意义的返回值,但它们最后都会返回一个CompletableFuture对象

public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main-*--------start-*-------");
        //将异步任务提交给线程池执行
        //runAsync不支持返回值
        CompletableFuture.runAsync(()->{
            System.out.println("当前线程号Runnable01:"+Thread.currentThread().getId());
        },executor);
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程号Callable01:" + Thread.currentThread().getId());
            return 100 / 0;
        }, executor).whenComplete((res,exception)->{
            System.out.println("异步任务完成了,结果是:"+res+"异常是:"+exception);
            System.out.println("使用的当前线程号是:"+Thread.currentThread().getId());
        }).exceptionally((exception)->{
            System.out.println("异步任务完成了,异常是:"+exception);
            return 10;
        });
        System.out.println("main-*--------end-*-------"+future.get());
    }

2、计算完成时回调方法

当CompletableFuture的计算结果完成或者抛出异常的时候,可以通过调用的whenComplete方法和exceptionally方法执行特定的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<Integer> future = CompletableFuture.supplyAsync(() -> {
        System.out.println("当前线程号Callable01:" + Thread.currentThread().getId());
        return 100 / 2;
    }, executor).whenComplete((res,exception) -> {
        System.out.println("获取当前的数据结果:"+res+"异常结果:"+exception);
    }).handle((res,exception)->{
        System.out.println("异步任务完成了,结果是:"+res+"异常是:"+exception);
        return 0;
    });
    System.out.println("main-*--------end-*-------"+future.get());
}

3、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, Executor executor)

和 complete 一样,可对结果做最后的处理(可处理异常),可改变返回值。

4、线程串行化方法

线程串行化的意思很明显就是同步执行,一个个执行,也就是前一个任务执行完毕,下一个任务才可以执行,哪怕方法带有ASync也是这样的,Async只能保证下一个任务会交给线程池去异步处理,而不是由上一个任务的线程接着执行下一个任务。

  • thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值。
  • thenApplyAsync:能接受上一步结果,有返回值
  • thenAccept 方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。
  • thenAcceptAsync:能接受上一步结果,但是无返回值
  • thenRun 方法:只要前面的任务执行完成,就开始执行thenRun,但它不能获取上一步的执行结果

带有 Async 默认是异步执行的(即任务交给线程池处理),同之前,以上方法执行都要前置任务成功完成。

Function

T:上一个任务返回结果的类型

U:当前任务的返回值类型

Tips:从这里开始以及下面的所有run,accept,apply方法都有相同的特性:

  • thenRun 方法:
    • 只要上面的任务执行完成,就开始执行 thenRun,不接收上一步结果,且无返回值。
  • thenAccept 方法:
    • 消费处理结果。接收任务的处理结果,并消费处理,无返回结果。
  • thenApply 方法:
    • 当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值。
public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main-*--------start-*-------");
        //串行化
        CompletableFuture.supplyAsync(() -> {
            System.out.println("任务一线程:" + Thread.currentThread().getId());
            int id = 10 / 4;
            System.out.println("运行结果:" + id);
            return id;
        }, executor).thenRunAsync(()->{
            //thenRunAsync无法感知上一步的结果
            System.out.println("任务2启动了");
        });
        System.out.println("main-*--------end-*-------");
    }
 public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main-*--------start-*-------");
        CompletableFuture.supplyAsync(() -> {
            System.out.println("任务一线程:" + Thread.currentThread().getId());
            int id = 10 / 4;
            System.out.println("运行结果:" + id);
            return id;
        }, executor).thenAcceptAsync((res) -> {
            System.out.println("任务2启动了:获取到任务1的内容"+res);
        });
        System.out.println("main-*--------end-*-------");
    }
public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main-*--------start-*-------");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务一线程:" + Thread.currentThread().getId());
            int id = 10 / 4;
            System.out.println("运行结果:" + id);
            return id;
        }, executor).thenApplyAsync((res) -> {
            System.out.println("任务2启动了:获取到任务1的内容" + res);
            return res + 5;
        });
        //阻塞式
        System.out.println("main-*--------end-*-------"+future.get());
    }

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

  • runAfterBoth

两个CompletionStage(顶级接口,也是CompletableFuture的父接口),两个任务都执行完毕才会执行下一步的操作(Runnable),这个方法不能获取前两个任务执行的结果,只需两个future 处理完任务后, 就会自动处理该任务。

这里说明一下两个组合任务,一个是方法的调用者一个是参数other,action是第三个要执行的任务。

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)
public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main-*--------start-*-------");
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
                System.out.println("任务一执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 100 / 2;
        }, executor);
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
                System.out.println("任务二执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 100 / 2;
        }, executor);
        future1.runAfterBothAsync(future2,()->{
            System.out.println("任务一和任务二执行完毕,任务三执行");
        });
        System.out.println("main-*--------end-*-------");
    }
  • thenAcceptBoth

当两个CompletionStage都执行完成后,把结果一块交给thenAcceptBoth进行处理。这个方法组合两个 future,可以获取两个 future 任务的返回结果(泛型T和U),然后处理任务,没有返回值。

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);
public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main-*--------start-*-------");
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
                System.out.println("任务一执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 100 / 2;
        }, executor);
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
                System.out.println("任务二执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 100 / 50;
        }, executor);
        future1.thenAcceptBothAsync(future2,(res1,res2)->{
            System.out.println("获取到任务一的值:"+res1);
            System.out.println("获取到任务二的值:"+res2);
        });
        System.out.println("main-*--------end-*-------");
    }
  • thenCombine

thenCombine 会把两个 CompletionStage 的任务都执行完成后,把两个任务的结果一块交给 thenCombine 来处理。它组合两个 future,获取两个 future 的返回结果,并返回当前任务的返回值

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,Ex
public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main-*--------start-*-------");
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
                System.out.println("任务一执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 100 / 2;
        }, executor);
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
                System.out.println("任务二执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 100 / 50;
        }, executor);
        CompletableFuture<Integer> future = future1.thenCombineAsync(future2, (res1, res2) -> {
            System.out.println("任务三开始执行:将任务一与任务二的值相加"+(res1 + res2));
            return res1 + res2;
        });
        System.out.println("main-*--------end-*-------"+future.get());
    }

6、两任务组合 - 一个完成

  • runAfterEither 方法:run开头的不接收上一步的结果,自己也无返回值

两个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);
public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main-*--------start-*-------");
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
                System.out.println("任务一执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 100 / 2;
        }, executor);
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
                System.out.println("任务二执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 100 / 50;
        }, executor);
        future1.runAfterEitherAsync(future2,()->{
            System.out.println("任务一与任务二执行了任意一个,开始任务三执行");
        });
        System.out.println("main-*--------end-*-------");
    }
  • acceptEither 方法

两个CompletionStage,谁执行返回的结果快,action就用那个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
public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main-*--------start-*-------");
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
                System.out.println("任务一执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "任务一执行:" + 100 / 2;
        }, executor);
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
                System.out.println("任务二执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "任务二执行:" + 100 / 50;
        }, executor);
        future1.acceptEitherAsync(future2, (res) -> {
            System.out.println("任务三开始执行,返回上一步执行内容为:" + res);
        });
        System.out.println("main-*--------end-*-------");
    }
  • applyToEither 方法:accept和apply两个开头的需要获取上一步的结果,但accept只接受无返回值,apply有返回值

两个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
public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main-*--------start-*-------");
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
                System.out.println("任务一执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "任务一执行:" + 100 / 2;
        }, executor);
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(2000);
                System.out.println("任务二执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "任务二执行:" + 100 / 50;
        }, executor);
        CompletableFuture<String> future = future1.applyToEitherAsync(future2, (res1) -> {
            System.out.println("任务三执行,获取上一步执行内容:" + res1);
            return "执行完毕" + res1;
        });
        System.out.println("main-*--------end-*-------" + future.get());
    }
  • thenCompose 方法

thenCompose 方法允许你对两个 CompletionStage 进行流水线操作,第一个操作完成时,将其结果作为参数传递给第二个操作。

public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) ;
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage

7、多任务组合

allOf:等待所有任务完成

anyOf:只要有一个任务完成

 CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
    System.out.println("查询商品的属性");
    return "黑色+256g";
}, executor);

CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(3000);
        System.out.println("查询商品的图片信息");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "hello.jpg";
}, executor);

CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
    System.out.println("查询商品的介绍");
    return "华为";
}, executor);
CompletableFuture<Void> allOf = CompletableFuture.allOf(futureAttr, futureImg, futureDesc);
allOf.get();//等待所有线程执行完
System.out.println("main.............end......." + futureAttr.get() + "=>" + futureImg.get() + "=>" + futureDesc.get());
CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
    System.out.println("查询商品的属性");
    return "黑色+256g";
},executor);

CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(3000);
        System.out.println("查询商品的图片信息");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "hello.jpg";
},executor);

CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
    System.out.println("查询商品的介绍");
    return "华为";
},executor);
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureAttr, futureImg, futureDesc);
System.out.println("main.............end......."+anyOf.get() );

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java异步并发线程池是一种用于管理和执行多线程异步任务的机制。通过使用线程池,可以有效地控制系统资源,并提高并发性能。核心线程数是线程池中一直存在的线程数量,它们准备就绪并等待异步任务的执行。可以使用ExecutorService接口的实现类Executors来创建线程池,例如使用newFixedThreadPool方法创建一个固定大小的线程池,如下所示:ExecutorService service = Executors.newFixedThreadPool(10); \[1\] 关于Java异步并发和线程池的更多信息,可以参考以下资源: - 参考1:https://wenku.baidu.com/view/a9cdf1c09889680203d8ce2f0066f5335a81672a.html - 参考2:https://www.cnblogs.com/weilx/p/16329743.html \[3\] #### 引用[.reference_title] - *1* *2* [Java中的异步线程池](https://blog.csdn.net/weixin_47409774/article/details/123610455)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Java异步并发和线程池](https://blog.csdn.net/qq_36330274/article/details/127229455)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值