一般来说 提高程序性能有以下两种方法
并行化
使用更多线程和硬件资源提高资源利用率
提高资源利用率
通常, 程序员们一开始都是以阻塞代码来实现业务需求. 直到出现性能瓶颈, 这时候 通常的做法是引入一些额外的线程 运行类似的阻塞代码 但这种扩展方式会引入资源竞争&并发问题 通常通过编写异步非阻塞代码可以解决上面的问题 提高资源利用率 Java原生提供了两种异步编程模型: Callback/Future. (jdk21正式发布了协程)
异步编程的几种方式
Callback
回调几乎没有组合能力,在复杂业务场景下, 每个函数调用都需要调用一个Callback, 缩进会一直增加, 很快就会导致代码难以阅读和维护(业界俗称为“Callback Hell”)。另外 如果想要处理异常 还得额外传入一个处理异常的Callback.
Future
Future会比回调好一点,但是Future在编排组合方面很难, 尽管java8有了CompletableFuture, 在编排组合上面有了一些可操作性, 但可操作性层面还是比较低的
尽管jdk8 里有了 CompletableFuture. 将多个 Future对象编排在一起是可行的,但也并不容易。
Reactive programming
具体介绍可以看 反应式编程介绍
一些优点
- 复杂异步任务的编排能力
- 与并发无关的高层次抽象
- 背压
- 惰性求值
- 声明式 一定程度上消除了callback hell
- 构建反应式系统的重要手段之一
一些缺点
- 学习曲线略显陡峭
- 相比命令式代码可读性比较低
协程
协程的几种实现方式
编译器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领域里主流的方向趋势有两个
- 反应式编程
- 协程(wisp/Project Loom)
当然反应式与协程并不是完全对立关系, 虽然在一些简单的异步场景下,协程会让响应式编程的适用性变窄(协程只是解决了并发&异步编程的问题). 虽然反应式编程是异步编程的一个子集, 但它并不止于此.
从本质上讲,协程只会让 .flatmap()
操作变得过时. 并不会让反应式编程过时, 反而会提高反应式的性能. 另外,真正对反应式编程产生威胁的是孵化中的结构化并发能力. 结构化并发引入了一种新的编排 并行&异步计算 的方法. 这也是反应式提供的核心能力之一, 但是结构化并发允许命令式编程范式, 没有反应式编程所带来编程负担.
所以在当前环境下 反应式编程和协程是相辅相成的.