谷粒商城微服务分布式高级篇(十二) —— 异步与线程池及异步编排

异步

在业务开发中,有很多异步场景,为了节约时间或或者提高系统的吞吐量,要做一些异步任务,在Java中要实现异步通常都是Thread,开启一个线程Thread,开启线程有四种方式。

创建线程四种方式

1)、继承Thread
2)、实现Runnable接口
3)、实现Callable接口+FutureTask(可以拿到返回结果,可以处理异常)
4)、线程池

方式1和方式2:主进程无法获取线程的运算结果。不适合我们当前的场景。
方式3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。

线程测试:

/**
 * @Description 线程测试
 */
public class ThreadTest {

    public static void main(String[] args) throws Exception {
        System.out.println("main...start...");

        //1)、继承Thread
        Thread01 thread01 = new Thread01();
        //启动线程
        thread01.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();
        System.out.println("返回值:" + integer.intValue());

        // 4)、线程池[ExecutorService] 给线程池直接提交任务。
        //ExecutorService service = Executors.newFixedThreadPool(3);
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                5,
                200,
                10L,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(10000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
        // 定时任务的线程池
        ExecutorService service = Executors.newScheduledThreadPool(2);
        service.execute(new Runable01());


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

    }

}

class Thread01 extends Thread {
    @Override
    public void run() {
        System.out.println("Thread当前线程:" + Thread.currentThread().getId());

        int i = 10 / 2;
        System.out.println("Thread运行结果:" + i);
    }
}

class Runable01 implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable当前线程:" + Thread.currentThread().getId());

        int i = 10 / 2;
        System.out.println("Runnable运行结果:" + i);
    }
}

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

执行结果:

main...start...
main...end...
Thread当前线程:13
Thread运行结果:5

可以看到开启线程,进入了异步,主程序已经结束,线程才打印。

线程池

JUC线程池

CompletableFuture异步编排

业务场景:
查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必然需要花费更多的时间。
在这里插入图片描述
假如商品详情页的每个查询,用户需要6.5s 后才能看到商品详情页的内容,这是显然不能接受的。
如果多个线程同时完成这个 6 步操作,也许只需要 1.5s 即可响应完成。

创建异步对象

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() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。

成功完成时回调方法

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

//返回相同的结果或例外,这一阶段的新completionstage,这个阶段完成时,执行特定动作的结果(或 null如果没有)和异常(或 null如果没有)这个阶段。
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action);
 
//返回相同的结果或例外,这一阶段的新completionstage,这个阶段完成时,执行特定动作执行给定的操作这一阶段的默认的异步执行设施,其结果(或 null如果没有)和异常(或 null如果没有)这个阶段作为参数。
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action);

//返回相同的结果或例外,这一阶段的新completionstage,这个阶段完成时,执行使用所提供的遗嘱执行人,给出的行动与结果(或 null如果没有)和异常(或 null如果没有)这个阶段作为参数。
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor);
 
//返回一个新的completablefuture已经完成与给定值。 
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn);

测试:

 /**
         * whenCompleteAsync 依赖于上一层的CompletableFuture 的返回值作为这个阶段的参数进行某些操作,返回的仍然是上一层的CompletableFuture
         * whenCompleteAsync和whenComplete的区别:
         *    whenCompleteAsync为异步,可能并不是执行上一层代码的线程
         *    whenComplete为执行上一层代码的线程执行这个阶段的逻辑
         *
         */
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 2;
            return i;
        }).whenCompleteAsync((t, u) -> {
            int m = t*2;  //可以利用回调值做一些操作计算
            System.out.println("上一层依赖的CompletableFuture返回的结果是:" + t);
            System.out.println("上一层依赖的CompletableFuture返回的异常结果是:" + u);
        });
        Integer result = future.get();
        System.out.println("CompletableFuture.whenCompleteAsync运行后结果是:"+result);

运行结果:
在这里插入图片描述

完成后处理方法

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:");
            return i;
        }, executor).handle(
                (t, u) -> {
                    if (t != null)//有返回值
                        return t * 2;
                    if (u != null)//发生异常
                        return 0;
                    return 0;
                }
        );
        Integer integer = future.get();
        System.out.println("main...end...  结果:" + integer);
    }

线程串行化方法

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

每一个方法都对应了三种操作。带有Async默认是异步执行的。这里所谓的异步指的是不在当前线程内执行。带有参数Executor executor的则用指定的线程池方案,不指定的话则用默认的ForkJoinPool.commonPool()

//​ T : 上一个任务返回结果的类型    U:当前任务的返回值的类型
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)

public CompletionStage<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 CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);

thenApplyAsync

CompletableFuture<Integer> thenApply = CompletableFuture.supplyAsync(() -> {
   return 100;
}).thenApplyAsync(t -> {
   return t * 2;
});
Integer result = thenApply.get();
System.out.println(result);//200

thenAcceptAsync

CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
            return 100;
        }).thenAcceptAsync(t -> {
            t = t*2;
            System.out.println("t========>"+t);//t========>200
        });
        System.out.println(future.get());//null

两任务组合 - 都要完成

两个任务必须都完成,触发该任务。

1、thenCombine:组合两个future,获取两个future任务的返回结果,并返回当前任务的返回值

2、thenAcceptBoth:组合两个future,获取两个future任务的返回结果,然后处理任务,没有返回值。

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

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

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

public <U,V> CompletableFuture<V> thenCombineAsync(
    CompletionStage<? extends U> other,
    BiFunction<? super T,? super U,? extends V> fn, Executor executor);
 组合两个future,获取两个future任务的返回结果,并返回当前任务的返回值   

public <U> CompletableFuture<Void> thenAcceptBoth(
    CompletionStage<? extends U> other,
    BiConsumer<? super T, ? super U> action);

public <U> CompletableFuture<Void> thenAcceptBothAsync(
    CompletionStage<? extends U> other,
    BiConsumer<? super T, ? super U> action);

public <U> CompletableFuture<Void> thenAcceptBothAsync(
    CompletionStage<? extends U> other,
    BiConsumer<? super T, ? super U> action, Executor executor);
组合两个future,获取两个future任务的返回结果,然后处理任务,没有返回值。

public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,
                                            Runnable action);

public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
                                                 Runnable action);

public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
                                                 Runnable action,
                                                 Executor executor);
组合两个future,不需要获取future的结果,只需两个future处理完任务后,处理该任务

thenAcceptBothAsync

CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1:" + Thread.currentThread().getId());
            int i = 10 / 2;
            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.thenAcceptBothAsync(future02,
                (f1, f2) -> {
                    System.out.println("任务3开始......之前的结果:" + f1 + "/" + f2);
                },
                executor);
CompletableFuture<String> future = future01.thenCombineAsync(future02,
        (f1, f2) -> {
            return f1 + ":" + f2;
        }
        , executor);

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

两任务组合_一个完成

当两个任务中,任意一个future任务完成的时候,执行任务

  • applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值.
  • acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值.
  • runAfterEither:两个任务有一个执行完成,不需要future的结果,处理任务,也没有返回值
public <U> CompletableFuture<U> applyToEither(
    CompletionStage<? extends T> other, Function<? super T, U> fn));

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

public <U> CompletableFuture<U> applyToEitherAsync(
    CompletionStage<? extends T> other, Function<? super T, U> fn,
    Executor executor));
    
 public CompletableFuture<Void> acceptEither(
    CompletionStage<? extends T> other, Consumer<? super T> action) );

public CompletableFuture<Void> acceptEitherAsync(
    CompletionStage<? extends T> other, Consumer<? super T> action));

public CompletableFuture<Void> acceptEitherAsync(
    CompletionStage<? extends T> other, Consumer<? super T> action,
    Executor executor) );

public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,
                                              Runnable action) );

public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
                                                   Runnable action));

public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
                                                   Runnable action,
                                                   Executor executor));

多任务组合

1、allOf:等待所有任务完成

2、anyOf:只要有一个任务完成

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);

public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值