Java 8原生API也可以开发响应式代码?

前段时间工作上比较忙,这篇文章一直没来得及写,本文是阅读《Java8实战》的时候,了解到Java 8里已经提供了一个异步非阻塞的接口(CompletableFuture),可以实现简单的响应式编程的模式,因此用这篇文章做个梳理。我是带着下面这几个问题去学习CompletableFuture这个接口的,
- CompletableFuture是为了解决什么问题而设计的?
- 它的使用场景是什么?开源软件中有实战使用案例吗?
- CompletableFuture的常用API都有哪些?如何使用?
- CompletableFuture和RxJava有什么不同?
这篇文章梳理下来,基本上可以回答前面四个问题,OK,我们进入正文。
基本概念

RPC(远程方法调用)的四种方式有:oneway、sync、future和callback,在dubbo或bolt这类通信框架中,默认使用的是sync模式(同步+阻塞),future和callback都属于异步模式,不过future模式在get的时候会阻塞,callback模式则不需要等待结果,有结果后服务端会回调请求方。
异步调用这类模式,比较适合的场景是IO密集型场景,要执行很多远程调用的任务,并且这些调用耗时可能比较久。以openwrite中的一个case为例:我发布一篇文章,需要给几个不同的写作平台创建文章,这时候我不希望这个过程是顺序的,就比较适合用异步调用模式。
Future模式除了在get()调用的时候会阻塞外,还有其他的局限性,例如:没有使用Java Lambda表达式的优势,对一连串的异步调用可以支持,但是写出来的代码会比较复杂。
CompletableFuture的常用API
阅读CompletableFuture的API的时候,我有一个体会——CompletableFuture之于Future,除了增加了回调这个最重要的特性,其他的特性有点像Stream对于集合迭代的增强。
使用CompletableFuture,我们可以像Stream一样使用一部调用,可以处理一些级联的异步调用(类似于Stream里的flatMap)、可以过滤一些无用的异步调用(anyOf、allOf)。
下面这张图是我按照自己的理解,梳理除了CompletableFuture常见的API,阅读的时候需要注意下面几个点:
- 把握几个大的分类:创建CompletableFuture、获取CompletableFuture的执行结果、主动结束CompletableFuture、异步调用任务的组合处理;
- 看着方法多,但是有规律可循,例如apply字样的接口,传入的方法参数都是有返回值的;
- 带either字样的,都是多个异步任务有一个满足条件即可的;
- 带executor方法的,都表示该方法可以用自定义的线程池来优化性能。

Dubbo项目中的使用案例
Dubbo对于异步化的支持起始在2.6.x中就有提供,是在发布bean的时候加个属性配置——async=true,然后利用上下文将异步标识一层层传递下去。在之前的公司中有一次排查dubbo(当时我们用的是dubbox)异步调用的问题,最后查到的原因就是多个异步调用,上下文里的信息串了。
Dubbo 2.7 中使用了 JDK1.8 提供的 CompletableFuture 原生接口对自身的异步化做了改进。CompletableFuture 可以支持 future 和 callback 两种调用方式。在Dubbo最新的master代码中,我知道了Dubbo的异步结果的定义,它的类图如下,可以看出AsyncRpcResult是一个CompletableFuture接口的实现。

实战Demo
通过下面的例子,可以看出CompletableFuture的最大好处——callback特性。首先定义一个接口,其中包括同步接口和该接口的异步版本。
public interface AsyncInterfaceExample {
    String computeSomeThine();
    CompletableFuture<String> computeSomeThingAsync();
}
然后定义该接口的实现类,可以看出,如果要讲现有的同步接口异步化,是比较容易的;
public class AsyncInterfaceExampleImpl implements AsyncInterfaceExample {
    @Override
    public String computeSomeThine() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "hello, world";
    }
    @Override
    public CompletableFuture<String> computeSomeThingAsync() {
        return CompletableFuture.supplyAsync(this::computeSomeThine);
    }
}
然后看下我们的测试case,如下:
public class AsyncInterfaceExampleTest {
    private static String getOtherThing() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "other";
    }
    public static void main(String[] args) {
        AsyncInterfaceExample asyncInterfaceExample = new AsyncInterfaceExampleImpl();
        //case1 同步调用
        long start = System.currentTimeMillis();
        String someThing = asyncInterfaceExample.computeSomeThine();
        String other = getOtherThing();
        System.out.println("cost:" + (System.currentTimeMillis() - start) + "  result:" + someThing + other);
        //case2 异步调用,使用回调
        start = System.currentTimeMillis();
        CompletableFuture<String> someThingFuture = asyncInterfaceExample.computeSomeThingAsync();
        other = getOtherThing();
        long finalStart = start;
        String finalOther = other;
        someThingFuture.whenComplete((returnValue, exception) -> {
            if (exception == null) {
                System.out.println(
                    "cost:" + (System.currentTimeMillis() - finalStart) + "  result:" + returnValue + finalOther);
            } else {
                exception.printStackTrace();
            }
        });
    }
}
上面这个案例的执行结果如下图所示:
 ***
***
本号(javaadu)专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。
Java 8原生API也可以开发响应式代码?的更多相关文章
- Android Java使用JavaMail API发送和接收邮件的代码示例
		JavaMail是Oracle甲骨文开发的Java邮件类API,支持多种邮件协议,这里我们就来看一下Java使用JavaMail API发送和接收邮件的代码示例 使用Javamail发送邮件,必需的j ... 
- Windows10 UWP开发 - 响应式设计
		Windows10 UWP开发 - 响应式设计 本篇随笔与大家简单讨论一下在开发适配不同分辨率.宽高比的Windows10 Universal App布局时的可行方式与小技巧.经验均从实践中总结, ... 
- 使用Bootstrap 3开发响应式网站实践07,页脚
		页脚部分比较简单,把一个12列的Grid切分. <footer> <div class="container"> <div class="r ... 
- 使用Bootstrap 3开发响应式网站实践06,使用ListGroup、Thumbnails展示内容
		□ ListGroup展示内容 当希望把同类型的内容以列表.区块展示的时候,ListGroup是不错的选择. <div class="col-sm-6"> <h3 ... 
- 使用Bootstrap 3开发响应式网站实践05,使用Tab、Modal、Form展示内容,使用Popover、Tooltip展示提示信息
		本篇体验用Tab插件显示内容.Html部分为: <div class="row" id="moreInfo"> <div class=&quo ... 
- 使用Bootstrap 3开发响应式网站实践04,使用Panels展示内容
		在Bootstrap页面中,通常用Panels来展示主要功能的内容.该部分Html为: <div class="row" id="featureHeading&qu ... 
- 使用Bootstrap 3开发响应式网站实践03,轮播下方的内容排版
		通常把一些重要信息.需要重点标注的信息放在轮播的下方显示,这部分区域用到了大字体的标题.副标题以及段落文字等. <div class="row" id="bigCa ... 
- 使用Bootstrap 3开发响应式网站实践02,轮播
		本篇体验图片轮播.html部分为: <div class="carousel slide" id="myCarousel" > <!--Ind ... 
- 使用Bootstrap 3开发响应式网站实践01,前期准备、导航区域等
		"使用Bootstrap 3开发响应式网站实践"系列,将使用Bootstrap 3.2制作一个自适应网站,无论是在电脑.平板,还是手机上,都呈现比较好的效果.在电脑浏览器上的最终效 ... 
随机推荐
- HDU 6019:MG loves gold(暴力set)
			http://acm.hdu.edu.cn/showproblem.php?pid=6019 题意:给出n个颜色的物品,你每次取只能取连续的不同颜色的物品,问最少要取多少次. 思路:从头往后扫,用se ... 
- java中session和application的用法
			Session的用法 首先创建2个jsp文件t1.jsp t2.jsp 在t1.jsp <% //设置session的键与值 session.setAttribute("abc&qu ... 
- 跟我学SpringCloud | 第十三篇:Spring Cloud Gateway服务化和过滤器
			SpringCloud系列教程 | 第十三篇:Spring Cloud Gateway服务化和过滤器 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich. ... 
- ElasticStack学习(六):ElasticSearch搜索初探
			一.ElasticSearch搜索介绍 1.ElasticSearch搜索方式主要分为以下两种: 1).URI Search:此种查询主要是使用Http的Get方法,在URL中使用查询参数进行查询: ... 
- 基于SpringBoot的Web API快速开发基础框架
			其实还是很因为懒,才会有这个案例项目的产生,每次开启一个终端的小服务都要整理一次框架,造成重复的.不必要的.缺乏创造性的劳动,SO,本着可以用.用着简单的原则上传代码到Github,希望有需要的朋友直 ... 
- Java底层技术系列文章-hashcode深入理解
			带着问题去理解: 1. Object类HashCode方法是如何实现的,和String类有什么区别? 2.HashCode和Equals之间的关系? 一.hashCode作用 hashCode方法返回 ... 
- Spring Cloud使用Zuul网关时报错
			当开启了Eureka集群后,每创建一个服务都要往这两个集群中进行注册否则访问时会产生500 
- GPS常识-A版(详)
			第一章 绪论 1.简述GPS系统的特点有哪些? GPS在测绘工程中应用的优点 P13 ●定位精度高 应用实践证明,相对静态定位1小时以上观测解,其平面位置:在300-1500m范围内,绝对误差小于1m ... 
- MyBatis从入门到精通(1):MyBatis入门
			作为一个自学Java的自动化专业211大学本科生,在学习和实践过程中"趟了不少雷",所以有志于建立一个适合同样有热情学习Java技术的参考"排雷手册". 最近在 ... 
- 异常:带有 CLSID {} 的 COM 对象无效或未注册
			今天处理调试打印程序的时候,看到这个异常: 代码: try { string strApplyEmpno=""; string strApplyDeptCode="&qu ... 
