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

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

通常, 程序员们一开始都是以阻塞代码来实现业务需求. 直到出现性能瓶颈, 这时候 通常的做法是引入一些额外的线程 运行类似的阻塞代码 但这种扩展方式会引入资源竞争&并发问题 通常通过编写异步非阻塞代码可以解决上面的问题 提高资源利用率 Java原生提供了两种异步编程模型: Callback/Future. (jdk21正式发布了协程)

异步编程的几种方式

Callback

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

        getUser(1L, new Callback<User>() {
            @Override
            public void onSuccess(User user) {
                getUser(user.parentUserId, new Callback<User>() {
                    @Override
                    public void onSuccess(User user) {
                        // do something
                    }
 
                    @Override
                    public void onError(Exception e) {
 
                    }
                });
            }
 
            @Override
            public void onError(Exception e) {
                e.printStackTrace();
            }
        });

Future

Future会比回调好一点,但是Future在编排组合方面很难, 尽管java8有了CompletableFuture, 在编排组合上面有了一些可操作性, 但可操作性层面还是比较低的

 

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

Reactive programming

具体介绍可以看 反应式编程介绍

一些优点

  1. 复杂异步任务的编排能力
  2. 与并发无关的高层次抽象
  3. 背压
  4. 惰性求值
  5. 声明式 一定程度上消除了callback hell
  6. 构建反应式系统的重要手段之一

一些缺点

  1. 学习曲线略显陡峭
  2. 相比命令式代码可读性比较低

协程

协程的几种实现方式

编译器CPS变换+状态机

通过新增一些关键字 (async/await or suspend) 在一些需要yield的地方标记 然后在编译器进行CPS变换+状态机的形式实现协程

基于虚拟机实现协程

在虚拟机层面对同步阻塞调用进行同步封装&栈操作实现协程. wisp&loom 都是基于JVM栈操作、结合Runtime对阻塞方法的支持以及加入调度器, 来实现的协程 让应用无需修改代码即可获取异步的性能

业界趋势&思考

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

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

  1. 反应式编程
  2. 协程(wisp/Project Loom)

当然反应式与协程并不是完全对立关系, 虽然在一些简单的异步场景下,协程会让响应式编程的适用性变窄(协程只是解决了并发&异步编程的问题). 虽然反应式编程是异步编程的一个子集, 但它并不止于此.

从本质上讲,协程只会让 .flatmap() 操作变得过时. 并不会让反应式编程过时, 反而会提高反应式的性能. 另外,真正对反应式编程产生威胁的是孵化中的结构化并发能力. 结构化并发引入了一种新的编排 并行&异步计算 的方法. 这也是反应式提供的核心能力之一, 但是结构化并发允许命令式编程范式, 没有反应式编程所带来编程负担.

所以在当前环境下 反应式编程和协程是相辅相成的.