Java 中的协程库 - Quasar
一、协程
一个进程可以产生许多线程,每个线程有自己的上下文,当我们在使用多线程的时候,如果存在长时间的 I/O 操作,线程会一直处于阻塞状态,这个时候会存在很多线程处于空闲状态,会造成线程资源的浪费。这就是协程适用的场景。
协程,其实就是在一个线程中,有一个总调度器,对于多个任务,同时只有一个任务在执行,但是一旦该任务进入阻塞状态,就将该任务设置为挂起,运行其他任务,在运行完或者挂起其他任务的时候,再检查待运行或者挂起的任务的状态,使其继续执行。
协程的方式更多用来做阻塞密集型(比如 I/O)的操作,计算密集型的还是使用线程更加合理。
Java 官方并没有协程库。但是伟大的社区提供了一个优秀的库,它就是 Quasar。
OpenJDK 在2018年创建了 Loom 项目,这是 Java 用来应对协程的官方解决方案,不过目前尚没有完整的发布日期,Java 用户可以期待一下。
二、Quasar 简介
Quasar 提供了高性能轻量级的线程,提供了类似 Go 的 channel,Erlang 的 actor,以及其它的异步编程的工具,可以用在 Java 和 Kotlin 编程语言中。
Quasar 最主要的贡献就是提供了轻量级线程的实现 —— fiber。Fiber 的功能和使用类似 Thread, API 接口也类似,所以使用起来没有违和感,但是它们不是被操作系统管理的,它们是由一个或者多个 ForkJoinPool 调度。一个空闲的 fiber 只占用 400 字节内存,切换的时候占用更少的 CPU,你的应用中可以有上百万的 fiber,显然Thread 做不到这一点。
Fiber 特别适合替换那些异步回调的代码。使用 FiberAsync 异步回调很简单,而且性能很好,扩展性也更高。
那么我们为什么称 Quasar 为协程库呢?实际上 Quasar 的实现就是想办法让运行中的线程栈停下来,好让 Quasar 的调度器介入,JVM 线程中断的条件只有两个:一个是抛异常;另外一个就是 return。这里 Quasar 就是通过抛异常(SuspendExecution)的方式来达到的,这样就完成了以线程的方式实现协程。
三、Quasar 配置
首先我们需要在 pom.xml 中引入 Quasar 的 jar 包(0.8.0 版本支持 jdk11 或更高的版本):
<dependency>
<groupId>co.paralleluniverse</groupId>
<artifactId>quasar-core</artifactId>
<version>0.7.4</version>
<classifier>jdk8</classifier>
</dependency>
Quasar fiber 依赖 java instrumentation 修改你的代码,可以在运行时通过 java Agent 实现,也可以在编译时使用 ant task实现。
通过 java agent 很简单,在程序启动的时候将下面的指令加入到命令行,注意把 path-to-quasar-jar.jar 替换成你实际的 quasar java 的地址:
-javaagent:path-to-quasar-jar.jar
-javaagent:C:\Users\Administrator\.m2\repository\co\paralleluniverse\quasar-core\0.7.3\quasar-core-0.7.3.jar
对于 maven 来说,你可以使用插件 maven-dependency-plugin,它会为你的每个依赖设置一个属性,以便在其它地方引用,我们主要想使用 ${co.paralleluniverse:quasar-core:jar}
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.5.1</version>
<executions>
<execution>
<id>getClasspathFilenames</id>
<goals>
<goal>properties</goal>
</goals>
</execution>
</executions>
</plugin>
然后你可以配置 exec-maven-plugin 或者 maven-surefire-plugin 加上 agent 参数,在执行 maven 任务的时候就可以使用 Quasar 了。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- Turn off before production -->
<argLine>-Dco.paralleluniverse.fibers.verifyInstrumentation=true</argLine>
<!-- Enable if using compile-time (AoT) instrumentation -->
<!-- argLine>-Dco.paralleluniverse.fibers.disableAgentWarning</argLine -->
<!-- Quasar Agent for JDK 7 -->
<!-- argLine>-javaagent:${co.paralleluniverse:quasar-core:jar}</argLine-->
<!-- Quasar Agent for JDK 8 -->
<argLine>-javaagent:${co.paralleluniverse:quasar-core:jar:jdk8}</argLine>
</configuration>
</plugin>
官方提供了一个 Quasar Maven archetype,你可以通过下面的命令生成一个quasar应用原型:
git clone https://github.com/puniverse/quasar-mvn-archetype
cd quasar-mvn-archetype
mvn install
cd ..
mvn archetype:generate -DarchetypeGroupId=co.paralleluniverse -DarchetypeArtifactId=quasar-mvn-archetype -DarchetypeVersion=0.7.4 -DgroupId=testgrp -DartifactId=testprj
cd testprj
mvn test
mvn clean compile dependency:properties exec:exec
如果你使用 gradle,可以看一下 gradle 项目模板:Quasar Gradle template project。
详细配置可以参考 specifying-the-java-agent-with-maven
四、Quasar 使用
Quasar 的核心是 Fiber 类,Fiber 继承自 Future,有一个返回值,类型为泛型 V,Fiber 的使用和 Thread 类似,
new Fiber<Void>() {
@Override
protected Void run() throws SuspendExecution, InterruptedException {
System.out.println("Hello Fiber");
return null;
}
}.start();
你可以传递 SuspendableRunnable 或 SuspendableCallable 给Fiber的构造函数:
new Fiber<>(() -> {
System.out.println("Hello Fiber");
return null;
}).start();
五、Comsat 介绍
Comsat 又是什么?
Comsat 还是 Parallel Universe 提供的集成 Quasar 的一套开源库,可以提供 web 或者企业级的技术,如 HTTP 服务和数据库访问。
Comsat 并不是一套 web 框架。它并不提供新的 API,只是为现有的技术如 Servlet、JAX-RS、JDBC 等提供 Quasar fiber 的集成。
它包含非常多的库,比如 Spring、ApacheHttpClient、OkHttp、Undertow、Netty、Kafka 等。
推荐阅读: