今天给大家介绍一个非常有意思的Java编程中的奇淫巧技——用CompletableFuture来做异步编程。
先说一下异步编程是啥,以及一般都是什么时候用他们。
其实大家比较常规习惯的都是用springboot+ssm+springcloud alibaba技术体系去开发一些web系统,然后里面填充各种crud代码,相信看这篇文章的朋友应该都差不多,毕竟天下苦crud久已,进软件开发行业之前觉得很高大上,进来以后发现技术都熟悉的差不多了,更多的都是在干crud。
那其实在这种常规性的crud类的系统和技术体系下,一般我们都不需要去做什么并发编程、异步编程之类的,因为一般系统原理都是springboot拉起来一个内嵌的web server,比如tomcat,然后对外接http请求,多线程并发处理请求,每个线程都是调用你的controller、service、dao和sql语句,是不是?
而且其实大家平时用spring的话,所有的spring bean都是做成单例的,而且单例一般都是无状态的,就是大家不会在内存里放什么数据,也就避免了多线程并发的安全问题,然后我们平时也就不用在crud系统里关注多线程问题了。
所以其实我们写的代码都是顺序流的模式,来一个请求,各种业务逻辑处理,调用service,dao,执行各种sql语句,然后最后都是面向数据库做增删改查,所以根本没机会也不需要使用并发编程和异步编程。
当然这种模式在crud业务类系统里还是不错的,毕竟方便,写业务就可以了,不要去关注复杂的多线程并发和异步化的问题。
所以并发编程和异步编程指的是什么呢?并发编程其实是指比如你写了一个系统,你的系统代码里不是执行那种crud顺序流代码,而是自己搞一个class,继承Thread,自己实现一个线程类,然后你的系统代码里拿到一个请求不是直接去crud,而是启动一个Thread线程去并发运行起来,这个线程不就会异步的去执行一些操作了。
虽然大家一般真的很少在业务系统里用这种并发编程和异步编程的模式,但是代码里确实是可以这么玩的。
好,那一般什么时候需要在代码里开一些线程去并发运行,异步化的运行呢?不幸的消息是,crud系统真的很少用,一般其实都是中间件类的系统会大量的运用并发编程的知识,各种请求都是异步化的执行,比如说大家可以去看看rocketmq、elasticsearch这一类中间件的源码,他们会经常用到。
或者说大家在自己公司里研发一些非crud类的系统,比如说一些公司内自研的一些底层系统,基础系统,中间件系统,其实也会经常用到并发编程,也就是异步编程的模式,那如果大家未来有可能用到异步编程的话,建议还是来了解一下今天的知识点,因为传统的异步编程其实控制你开的那个线程其实真的很麻烦。
但是用了CompletableFuture之后,对你开出来的多线程并发任务,你其实是可以很好的去控制他们的,一起开始今天的旅程吧。
在Java的世界里,处理异步和多线程任务一直是个让人头疼的问题。传统的线程创建和管理方式,不仅代码繁琐,还容易出错。但是,自从Java 8推出了CompletableFuture这个神器,一切都变得不一样了。它以一种极其优雅的方式,解决了异步编程的诸多痛点。今天,咱们就来聊聊这个CompletableFuture,看看它到底是如何让异步多线程编程变得如此优雅的。
一、异步编程的痛点
在CompletableFuture出现之前,Java的异步编程主要有两种方式:通过Future接口和实现Callable接口。但是,这两种方式都存在一些问题。
1、阻塞和轮询:使用Future.get()方法获取异步结果时,如果结果还没有准备好,当前线程会被阻塞。为了避免阻塞,我们通常会使用轮询的方式检查结果是否准备好,但这种方式会浪费CPU资源。
2、无法组合多个异步任务:在实际开发中,我们经常需要组合多个异步任务的结果。但是,使用传统的Future和Callable,很难实现复杂的异步任务组合逻辑。
3、异常处理不便:当异步任务出现异常时,传统的处理方式是通过Future.get()方法捕获异常,但这种方式不够灵活,也不便于异步任务的错误恢复。
二、CompletableFuture的优雅之处
CompletableFuture是Java 8引入的一个新的异步编程工具,它解决了传统异步编程方式的诸多痛点,让异步多线程编程变得更加优雅和便捷。
1、非阻塞的异步结果获取:CompletableFuture提供了非阻塞的异步结果获取方式。你可以通过thenApply、thenAccept、thenRun等方法,在异步任务完成时执行特定的操作,而无需阻塞当前线程。
2、灵活的异步任务组合:CompletableFuture提供了丰富的API,支持多种异步任务的组合方式。你可以使用thenCombine、thenAcceptBoth、runAfterBoth等方法,将两个异步任务的结果进行组合;或者使用allOf、anyOf等方法,等待多个异步任务完成。
3、便捷的异常处理:CompletableFuture提供了exceptionally方法,用于处理异步任务中出现的异常。你可以在这个方法中定义异常的处理逻辑,使得异步任务的错误恢复变得更加便捷。
4、链式调用和流式处理:CompletableFuture的方法调用支持链式操作,你可以将多个异步任务串联起来,形成一个处理流程。这种链式调用和流式处理的方式,使得异步编程的代码更加简洁和易读。
三、CompletableFuture的使用示例
下面,我们通过一些具体的示例,来看看CompletableFuture是如何优雅地解决异步多线程编程的问题的。
示例1:异步执行任务并获取结果
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 模拟异步任务
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello, CompletableFuture!";
});
// 非阻塞方式获取结果
future.thenAccept(System.out::println);
在这个示例中,我们使用supplyAsync方法异步执行了一个任务,并返回了一个CompletableFuture对象。然后,我们使用thenAccept方法,在异步任务完成时打印结果。这种方式避免了阻塞和轮询,使得代码更加简洁和高效。
示例2:组合多个异步任务的结果
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
future1.thenCombine(future2, (result1, result2) -> result1 + " " + result2)
.thenAccept(System.out::println);
在这个示例中,我们使用thenCombine方法将两个异步任务的结果进行了组合。当两个异步任务都完成时,它们的结果会被拼接成一个新的字符串,并打印出来。这种方式使得异步任务的组合变得更加灵活和便捷。
示例3:处理异步任务中的异常
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
if (true) { // 假设这里有个条件判断,满足时抛出异常
throw new RuntimeException("Oops!");
}
return "Normal result";
});
future.exceptionally(ex -> "Fallback result")
.thenAccept(System.out::println);
在这个示例中,我们使用exceptionally方法处理了异步任务中出现的异常。当异步任务抛出异常时,我们会返回一个备用结果,并打印出来。这种方式使得异步任务的错误恢复变得更加容易和可控。
四、CompletableFuture的高级特性
除了上述的基本用法外,CompletableFuture还提供了一些高级特性,进一步增强了其异步编程的能力。
1、自定义线程池:默认情况下,CompletableFuture会使用ForkJoinPool.commonPool()作为线程池来执行异步任务。但是,你也可以通过supplyAsync、runAsync等方法的重载版本,自定义线程池来执行异步任务。
2、完成时的回调:CompletableFuture提供了whenComplete、whenCompleteAsync等方法,允许你在异步任务完成时执行特定的回调操作。这些回调操作可以处理正常结果,也可以处理异常情况。
3、结果计算完成时的通知:CompletableFuture还提供了thenRun方法,允许你在异步任务的结果计算完成时执行特定的操作。这个方法不关心异步任务的结果,只关心任务是否完成。
4、等待多个异步任务完成:CompletableFuture提供了allOf、anyOf等静态方法,用于等待多个异步任务完成。allOf会等待所有任务完成,而anyOf则只等待其中一个任务完成。
五、总结
CompletableFuture是Java 8引入的一个强大的异步编程工具,它以一种极其优雅的方式解决了传统异步编程方式的诸多痛点。通过使用CompletableFuture,我们可以更加便捷地实现异步多线程编程,提高代码的可读性和可维护性。同时,CompletableFuture还提供了丰富的高级特性,进一步增强了其异步编程的能力。因此,如果你还在为异步多线程编程而苦恼,不妨尝试一下CompletableFuture,相信它会给你带来全新的编程体验。