响应式编程是一种面向数据流和变化传播的编程范式.

异步编程的几种方式

一般来说 提高程序性能有以下两种方法

  1. 并行化 使用更多线程和硬件资源
  2. 提高资源利用率 提高资源利用率

通常, 程序员们一开始都是以阻塞代码来实现业务需求. 直到出现性能瓶颈, 这时候 通常的做法是引入一些额外的线程 运行类似的阻塞代码 但这种扩展方式会引入资源竞争&并发问题

通常通过编写异步非阻塞问题可以解决上面的问题 提高资源利用率 Java原生提供了两种异步编程模型: Callback/Future

Callback

回调几乎没有组合能力,在复杂业务场景下, 每个函数调用都需要调用一个Callback, 缩进会一直增加, 很快就会导致代码难以阅读和维护(业界俗称为“Callback Hell”)。另外 如果想要处理异常 还得额外传入一个处理异常的Callback.

 

Future

Future对象比回调好一点,但它们在组合方面仍然表现不佳

 

尽管jdk8 里有了 CompletableFuture. 将多个 Future对象编排在一起是可行的,但也并不容易。

Reactive

Callback&Future 是最简单最基础的响应式. 但他们面向的都是单值

协程

上述三种异步编程(Callback/Future/Reactive)的难点 正如 Dean Gaudet(Apache开发者)所说:“其内在的复杂性——将线性思维分解成一堆回调的负担(breaking up linear thought into a bucketload of callbacks)——仍然存在, 协程旨在使用同步阻塞编程模型解决传统的异步编程模式(Callback/Future/Reactive), 以更接近自然编程模式(线性思维).

在了解协程之前, 我们先来看一下CPS(Continuation-passing style)

CPS-(Continuation-passing style)

CPS是函数式编程中一个古老的概念, 最初是在1970年代作为一种编程风格出现, 现在, CPS作为反应式系统的编程风格而被再次发掘出来.

首先, 我们先来看一下一般的编程风格.

    public static void main(String[] args) {
        print(add(1, 2)); // 通常我们会这么调用方法
    }
 
    public static int add(int a, int b) {
        return a + b;
    }
 
    public static void print(int num) {
        System.out.println(num);
    }

上面的代码重构成CPS风格的代码则如下(这个过程通常称为CPS变换):

    public static void main(String[] args) {
        add(1, 2, value -> print(value));
    }
 
    public static void add(int a, int b, Continuation<Integer> cont) {
        cont.run(a + b);
    }
 
    public static void print(int num) {
        System.out.println(num);
    }
 
    interface Continuation<T> {
        void run(T value);
    }

CPS变换即将程序的后半部分构造成一个 Continuation, 通过函数参数进行传递, 在未来某个时刻调用它, 以运行程序的后半部分, ’contionuation‘ 一般晦涩的翻译成 “计算续体”, 通常也叫做 “程序的剩下部分”. 大家这里也能看到, contionuation 和上面的 Callback 其实很像, 只不过换了一个叫法.

Callback/Future/Reactive 本质上都是在手动构造 contionuation, 进行CPS变换, 而协程则是通过一些方式进行自动CPS变换, 减少我们的编程负担.

java中实现协程的几种方式

  • async/await
  • jvm层面内置协程

    在阻塞调用的地方 jvm内部进行CPS变换

业界趋势&思考

在异步编程方向上, 现在java领域里比较主流的方向有两个

  1. 反应式编程
  2. 协程

当然反应式与协程并不是完全对立关系, 虽然在简单的异步场景下,协程会让响应式编程的适用变小. 虽然反应式编程是异步编程的一个子集, 但它不仅仅只是为了异步编程. 他还具有以下几个重要特性

  1. 针对复杂异步流的编排以及组合能力
  2. 与并发无关的高层次抽象