Java Reactive 异步与并发编程
【摘要】Reactive 编程在多数人眼中是异步、并发的“银弹/神器”。本文分析了Reactive 执行原理,说明 Reactive 编程是数据驱动的,而不是“事件”驱动的。Reactive 编程分为数据源准备、数据流建模、调度者分配三个基本设计步骤,才能实现异步并发执行。最后,我们给出基于数据流图的计算模型的设计与编程方案。
大数据和云计算(云服务)让 Reactive 编程成为新一代的编程神器。尽管 Reactive 编程模型大大简化了异步与并发编程的难度,但绝不是低门槛的。它首先需要你改变传统顺序处理的计算模式,建立面向数据流的计算模型;然后,需要有强大的线程、协程等并发知识,才能编写出 safe 的应用;再者还需要一些函数式编程的知识,如 Lambda、闭包等。本文努力描述响应式编程需要的最基础的知识,并用一些案例,让你体验 Reactive 编程的神奇与优雅。
1、准备知识
Java Reactive 编程使用 RxJava 库,虽然可以兼容 Java 5 编程,但你会失去 Java 8 给你带来的便利,如 Lambda 表达式,CompleteableFuture 等异步特性的支持。关键是没有 Lambda 函数,Reactive Java 程序几乎无法阅读!
1.1 编程环境配置
1. 项目文件配置
maven 配置文件 pom.xml 需要
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF8</encoding>
</configuration>
</plugin>
</plugins>
</build>
2. IDEA 设置
Intellij IDEA 要设置三处位置,且要求版本一致,否则就有出错提示,例如:“source Release 8 Requires Target Release 1.8…”
- File -> Project Structure -> Project
- File -> Project Structure -> Modules
- File -> Setting -> Build -> Compiler -> Java Compiler
1.2 Lambda 表达式
现在,不支持 Lambda 表达式的语言真不多。 在 Java 中,它主要作为单一方法的接口匿名实现。例如:
public class TestLambda {
public static void main(String[] args) {
System.out.println("=== RunnableTest ===");
// Anonymous Runnable
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello world one!");
}
};
// Lambda Runnable
Runnable r2 = () -> System.out.println("Hello world two!");
// Run em!
r1.run();
r2.run();
}
}
Lambda 表达式 的语法,例如:
Argument List | Arrow Token | Body |
---|---|---|
(int x, int y) | -> | x + y |
官方教程: Java SE 8: Lambda Quick Start
2、Future<V>
与多线程编程
Future<V>
是一个泛型接口,如果一个可运行的函数(实现 Callable 或 Runable 的类)在一个线程中运行,利用 Future<V>
可以用它的 get() 方法返回 V 类型的结果。 注意, get() 会阻塞当前线程。例如:
public class TestFuture {
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html
static ExecutorService executor = Executors.newCachedThreadPool();
public void testTaskRunning(String name, Integer t) {
System.out.println("Prepare for execution:" + name);
long startTime = System.currentTimeMillis(); //获取开始时间
// using lambda may cause 10X time then Callable
// Future<String> fa = executor.submit(
// new Callable<String>() {
// @Override
// public String call() throws Exception {
// try {
// Thread.sleep(t);