Java多线程--CompletableFuture异步编排

简介

回顾创建线程的几种方式

  1. 通过继承Thread类或实现Runnable接口,重写run方法来创建一个线程,通过调用线程实例的start方法启动线程执行;
  2. 实现Callable接口,比起第一种方式,这种方式的好处就是配合FutureTask对象,可以得到线程执行完毕的返回结果。
    	public class ThreadTest {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            MyTread myTread = new MyTread();
            FutureTask<Integer> futureTask = new FutureTask<>(myTread);
            futureTask.run();
            // 阻塞式方法 会等待线程执行完毕获取返回结果
            Integer i = futureTask.get();
            System.out.println(i); 	// 20
        }
    
        public static class MyTread implements Callable<Integer> {
            @Override
            public Integer call() throws Exception {
                System.out.println("线程开始执行...");
                int i = 100 / 5;
                System.out.println("线程执行完毕...");
                return i;
            }
        }
    }
    

CompletableFuture的作用

上述一些原生实现线程的方式存在很多的局限性,比如不能对一个任务(方法)内的多个操作进行异步、组合等操作。CompletableFuture实现了Future接口,它可以对我们方法内的操作进行异步编排的能力,并且可以指定各个异步操作的执行顺序,也就是线程串行化
举一个形象的例子:
在一个方法中实现了A、B、C三个功能,假设每个功能的执行所需要的时间分别为(5s,10s,15s),在没有异步编排之前,进入方法依次执行 A功能—>B功能—>C功能,执行方法完毕,用时30s。当我们在方法中引入异步编排,给A、B、C每个功能都分配一个不同的线程执行,这样的情况下,最快15s这个方法就可以执行完毕,这就是所谓的异步
另一个应用场景:
B功能必须要依赖A功能执行完毕,我们也可以使用CompletableFuture来指定只有A功能执行完毕才能执行B功能,这就是所谓的线程串行化

CompletableFuture用法

注意:以下所有带Async的方法都表示执行下一步操作会去线程池中寻找空闲的线程执行,也就是异步执行(如果使用相同的线程池,也可能是相同的线程执行)。不带Async执行任务的线程和执行完该任务去执行下一步相应的操作使用的线程是相同的;

创建线程

CompletableFuture创建线程的四种方式:
在这里插入图片描述

  • 前两种可以获取线程执行完毕的返回值,后两者不可以;
  • 带有Executor参数的可以传入自定义的线程池,不传则使用默认的。
  1. runAsync(Runnable runnable, Executor executor)
    	public class ThreadTest02 {
        // 自定义线程池
        public static final ExecutorService executorService = Executors.newFixedThreadPool(10);
    
        public static void main(String[] args) {
            CompletableFuture.runAsync(()->{
                System.out.println("线程开始执行...");
                int i = 100/5;
                System.out.println("运行结果:" + i);	//运行结果:20
            }, executorService);
        }
    }
    
  2. supplyAsync(Supplier<U> supplier, Executor executor)
    	public class ThreadTest02 {
        // 自定义线程池
        public static final ExecutorService executorService = Executors.newFixedThreadPool(10);
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
                System.out.println("线程开始执行...");
                int i = 100 / 5;
                System.out.println("运行结果:" + i);	//运行结果:20
                return i;
            }, executorService);
            // 线程执行完毕的返回值
            Integer i = future.get();
            System.out.println("获取到返回值:" + i);	// 获取到返回值:20
        }
    }
    

线程任务完成时回调方法

方法:
在这里插入图片描述

  • whenComplete可以处理正常和异常的计算结果;exceptionally 处理异常情况;
  • whenComplete:执行当前任务的线程继续来执行whenComplete的任务;
    - whenCompleteAsync:将 whenCompleteAsync需要执行的任务交给线程池来进行执行;
  • 不带Async执行任务的线程和执行完任务去执行回调方法whenComplete使用的线程是相同的;带Async执行回调方法会去线程池中寻找空闲的线程执行(如果使用相同的线程池,也可能是相同的线程执行)。
  1. whenCompleteAsync:
    public class ThreadTest02 {
        // 自定义线程池
        public static final ExecutorService executorService = Executors.newFixedThreadPool(10);
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
                System.out.println("线程开始执行...");
                int i = 100 / 5;
                System.out.println("运行结果:" + i);
                return i;
            }, executorService).whenCompleteAsync((result, exception) -> {
                System.out.println("线程执行完毕, 返回结果:" + result +  "异常:" + exception);
                // 线程执行完毕, 返回结果:20异常:null
            });
        }
    }
    
  2. exceptionally:
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("线程开始执行...");
            int i = 100 / 0;
            System.out.println("运行结果:" + i);
            return i;
        }, executorService).whenCompleteAsync((result, exception) -> {
            System.out.println("线程执行完毕, 返回结果:" + result +  "异常:" + exception);
            //线程执行完毕, 返回结果:null异常:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
        }).exceptionally(throwable -> {
            return 33;
        });
        Integer i = future.get();
        System.out.println("获取到返回值:" + i);	// 获取到返回值:33
    }
    

    当执行的任务中存在异常,使用exceptionally可以改变返回结果(whenComplete可以处理正常和异常的计算结果,但不能修改返回果)。

handle

和exceptionally一样,可以对任务执行完毕进行后续处理,不同的是该方法在任务正常或异常都可以对其进行后续处理。

public static void main(String[] args) throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        System.out.println("线程开始执行...");
        int i = 100 / 20;
        System.out.println("运行结果:" + i);
        return i;
    }, executorService).whenCompleteAsync((result, exception) -> {
        System.out.println("线程执行完毕, 返回结果:" + result +  "异常:" + exception);
        // 线程执行完毕, 返回结果:5异常:null
    }).handle((result, throwable) -> {
        Integer rs = null;
        if (result != null) {
            rs = result * 2;
        } else if (throwable != null) {
            rs = 0;
        }
        return rs;
    });
    Integer i = future.get();
    System.out.println("获取到返回值:" + i);	// 获取到返回值:10
}

线程串行化

比如A,B两个任务,B任务必须要等待A任务执行完毕后才能执行,我们就要使这两个任务串行化执行。
方法:
在这里插入图片描述

  • thenRun:只要上一个任务执行完毕,就开始执行thenRun任务;
  • thenAccept:上一个任务执行完毕后,接收它的返回值,再开始执行thenAccept任务;
  • thenApply:上一个任务执行完毕后,接收它的返回值,执行thenApplyAsync任务,并返回当前任务的返回值。
  1. thenRun:
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务A开始执行...");
            int i = 100 / 20;
            System.out.println("任务A执行完毕, 运行结果:" + i);
            return i;
        }, executorService).thenRun(() -> {
            System.out.println("任务B开始执行...");
        });
    }
    

    output:
    任务A开始执行…
    任务A执行完毕, 运行结果:5
    任务B开始执行…

  2. thenAccept:
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务A开始执行...");
            int i = 100 / 20;
            System.out.println("任务A执行完毕, 运行结果:" + i);
            return i;
        }, executorService).thenAccept((result) -> {
            System.out.println("任务B开始执行, 接受到任务A的返回结果:" + result);
        });
    }
    

    output:
    任务A开始执行…
    任务A执行完毕, 运行结果:5
    任务B开始执行, 接受到任务A的返回结果:5

  3. thenApply:
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务A开始执行...");
            int i = 100 / 20;
            System.out.println("任务A执行完毕, 运行结果:" + i);
            return i;
        }, executorService).thenApply((result) -> {
            System.out.println("任务B开始执行, 接受到任务A的返回结果:" + result);
            return 20;
        });
        Integer i = future.get();
        System.out.println("获取到任务B的返回值:" + i);
    }
    

    output:
    任务A开始执行…
    任务A执行完毕, 运行结果:5
    任务B开始执行, 接受到任务A的返回结果:5
    获取到任务B的返回值:20

两任务组合–都要完成

两个任务必须都要完成,触发该任务。
方法:
在这里插入图片描述在这里插入图片描述
thenCombine:

public static void main(String[] args) throws ExecutionException, InterruptedException {
    CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
        System.out.println("任务A开始执行...");
        System.out.println("任务A执行完毕...");
        return "A";
    }, executorService);

    CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> {
        System.out.println("任务B开始执行...");
        System.out.println("任务B执行完毕...");
        return "B";
    }, executorService);

    CompletableFuture<String> futureC = futureA.thenCombine(futureB, (firstResult, secondResult) -> {
        System.out.println("任务C开始执行...");
        System.out.println("接收到任务A的返回值:" + firstResult);
        System.out.println("接收到任务B的返回值:" + secondResult);
        return "C";
    });

    String i = futureC.get();
    System.out.println("获取到任务C的返回值:" + i);
}

output:
任务A开始执行…
任务A执行完毕…
任务B开始执行…
任务B执行完毕…
任务C开始执行…
接收到任务A的返回值:A
接收到任务B的返回值:B
获取到任务C的返回值:C

两任务组合–完成一个

两个任务中,其中一个完成,触发该任务执行。
方法:
在这里插入图片描述
在这里插入图片描述
applyToEither:

public static void main(String[] args) throws ExecutionException, InterruptedException {
    CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
        System.out.println("任务A开始执行...");
        System.out.println("任务A执行完毕...");
        return "A";
    }, executorService);

    CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> {
        System.out.println("任务B开始执行...");
        System.out.println("任务B执行完毕...");
        return "B";
    }, executorService);

    CompletableFuture<String> futureC = futureA.applyToEither(futureB, (result) -> {
        System.out.println("任务C开始执行...");
        System.out.println("接收到执行完毕的任务的返回值:" + result);
        return "C";
    });

    String i = futureC.get();
    System.out.println("获取到任务C的返回值:" + i);
}

多任务组合

方法:
在这里插入图片描述

  • allOf:必须等待所有的任务都执行完毕,才能继续执行,否则阻塞;
  • anyOf:只要其中一个任务执行完毕,则继续执行。
  1. allOf

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务A开始执行...");
            System.out.println("任务A执行完毕...");
            return "A";
        }, executorService);
    
        CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务B开始执行...");
            System.out.println("任务B执行完毕...");
            return "B";
        }, executorService);
    
        CompletableFuture<String> futureC = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务C开始执行...");
            System.out.println("任务C执行完毕...");
            return "C";
        }, executorService);
    
        // 直到所有任务都执行完毕 才能继续往下执行
        CompletableFuture<Void> allOf = CompletableFuture.allOf(futureA, futureB, futureC);
        allOf.get();
    
        System.out.println("A:" + futureA.get() + "B:" + futureB.get() + "C:" + futureC.get());
    }
    

    output:
    任务A开始执行…
    任务A执行完毕…
    任务B开始执行…
    任务B执行完毕…
    任务C开始执行…
    任务C执行完毕…
    A:AB:BC:C

  2. anyOf

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> futureC = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务C开始执行...");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务C执行完毕...");
            return "C";
        }, executorService);
    
        // 有一个任务执行完毕 则继续往下执行
        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureA, futureB, futureC);
        anyOf.get();
        System.out.println("执行完毕任务的返回值:" + anyOf.get());
    }
    

    output:
    任务A开始执行…
    任务A执行完毕…
    任务B开始执行…
    任务B执行完毕…
    执行完毕任务的返回值:A
    任务C开始执行…
    任务C执行完毕…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值