一、協程
一個進程可以產生許多線程,每個線程有自己的上下文,當我們在使用多線程的時候,如果存在長時間的 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 等。
推薦閱讀:
