Java協(xié)程編程之Loom項(xiàng)目實(shí)戰(zhàn)記錄
前提
之前很長(zhǎng)一段時(shí)間關(guān)注JDK
協(xié)程庫(kù)的開發(fā)進(jìn)度,但是前一段時(shí)間比較忙很少去查看OpenJDK
官網(wǎng)的內(nèi)容。Java
協(xié)程項(xiàng)目Loom
(因?yàn)轫?xiàng)目還在開發(fā)階段,OpenJDK
給出的官網(wǎng)https://openjdk.java.net/projects/loom
中只有少量Loom
項(xiàng)目相關(guān)的信息)已經(jīng)在2018
年之前立項(xiàng),目前已經(jīng)發(fā)布過基于JDK17
編譯和JDK18
編譯等早期版本,筆者在下載Loom
早期版本的時(shí)候只找到JDK18
編譯的版本:
下載入口在:https://jdk.java.net/loom
由于該JDK
版本過高,目前可以使用主流IDE
導(dǎo)入Loom-JDK-18+9
進(jìn)行代碼高亮和語(yǔ)法提醒,暫時(shí)找不到方法進(jìn)行編譯,暫時(shí)使用該JDK
執(zhí)行目錄下的的javac
命令腳本進(jìn)行編譯,使用java
命令腳本運(yùn)行。
Loom項(xiàng)目簡(jiǎn)單介紹
Loom - Fibers, Continuations and Tail-Calls for the JVM
Loom
項(xiàng)目的標(biāo)題已經(jīng)凸顯了引入的三大新特性:
Fibers
:幾年前看過當(dāng)時(shí)的Loom
項(xiàng)目的測(cè)試代碼就是使用Fiber
這個(gè)API
(現(xiàn)在這個(gè)API
已經(jīng)被移除),意為輕量級(jí)線程,即協(xié)程,又稱為輕量級(jí)用戶線程,很神奇的是在目前的JDK
中實(shí)際上稱為Virtual Thread
(虛擬線程)Continuations
:直譯為"連續(xù)",實(shí)現(xiàn)上有點(diǎn)像閉包,參考不少資料,尚未準(zhǔn)確理解其具體含義,感覺可以"粗暴"解讀為"程序接下來要執(zhí)行什么"或者"下一個(gè)要執(zhí)行的代碼塊"Tail-Calls
:尾調(diào)用VM
級(jí)別支持
三個(gè)新特性不詳細(xì)展開,目前只是EA
版本,還存在修改的可能性,所以也沒必要詳細(xì)展開。
Virtual Thread使用
當(dāng)前版本Loom
項(xiàng)目中協(xié)程使用并沒有引入一個(gè)新的公開的虛擬線程VirtualThread
類,雖然真的存在VirtualThread
,但這個(gè)類使用default
修飾符,隱藏在java.lang
包中,并且VirtualThread
是Thread
的子類。協(xié)程的創(chuàng)建API
位于Thread
類中:
使用此API
創(chuàng)建協(xié)程如下:
public static void main(String[] args) { Thread fiber = Thread.startVirtualThread(() -> System.out.println("Hello Fiber")); }
從當(dāng)前的源碼可知:
VirtualThread
會(huì)通過Thread.currentThread()
獲取父線程的調(diào)度器,如果在main
方法運(yùn)行,那么上面代碼中的協(xié)程實(shí)例的父線程就是main
線程- 默認(rèn)的調(diào)度器為系統(tǒng)創(chuàng)建的
ForkJoinPool
實(shí)例(VirtualThread.DEFAULT_SCHEDULER
),輸入的Runnable
實(shí)例會(huì)被封裝為RunContinuation
,最終由調(diào)度器執(zhí)行 - 對(duì)于
timed unpark
(正在阻塞,等待喚醒)的協(xié)程,使用系統(tǒng)創(chuàng)建的ScheduledExecutorService
實(shí)例進(jìn)行喚醒 - 這個(gè)靜態(tài)工廠方法創(chuàng)建完協(xié)程馬上運(yùn)行,返回的是協(xié)程實(shí)例
如果按照上面的Thread.startVirtualThread()
方法去創(chuàng)建協(xié)程,顯然無法定義協(xié)程的名稱等屬性。Loom
項(xiàng)目為Thread
類引入了建造者模式,比較合理地解決了這個(gè)問題:
// 創(chuàng)建平臺(tái)線程建造器,對(duì)應(yīng)于Thread實(shí)例 public static Builder.OfPlatform ofPlatform() { return new ThreadBuilders.PlatformThreadBuilder(); } // 創(chuàng)建虛擬線程建造器,對(duì)應(yīng)于VirtualThread public static Builder.OfVirtual ofVirtual() { return new ThreadBuilders.VirtualThreadBuilder(); }
簡(jiǎn)單說就是:
ofPlatform()
方法用于構(gòu)建Thread
實(shí)例,這里的Platform Thread
(平臺(tái)線程)其實(shí)就是JDK1.0
引入的線程實(shí)例,普通的用戶線程ofVirtual()
方法用于構(gòu)建VirtualThread
實(shí)例,也就是構(gòu)建協(xié)程實(shí)例
這兩個(gè)建造器實(shí)例的所有Setter
方法鏈展開如下:
public static void main(String[] args) { Thread.Builder.OfPlatform platformThreadBuilder = Thread.ofPlatform() // 是否守護(hù)線程 .daemon(true) // 線程組 .group(Thread.currentThread().getThreadGroup()) // 線程名稱 .name("thread-1") // 線程名稱前綴 + 起始自增數(shù)字 => prefix + start,下一個(gè)創(chuàng)建的線程名稱就是prefix + (start + 1) // start > 0的情況下會(huì)覆蓋name屬性配置 .name("thread-", 1L) // 是否啟用ThreadLocal .allowSetThreadLocals(false) // 是否啟用InheritableThreadLocal .inheritInheritableThreadLocals(false) // 設(shè)置優(yōu)先級(jí) .priority(100) // 設(shè)置線程棧深度 .stackSize(10) // 設(shè)置未捕獲異常處理器 .uncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { } }); // thread-1 Thread firstThread = platformThreadBuilder.unstarted(() -> System.out.println("Hello Platform Thread First")); // thread-2 Thread secondThread = platformThreadBuilder.unstarted(() -> System.out.println("Hello Platform Thread Second")); Thread.Builder.OfVirtual virtualThreadBuilder = Thread.ofVirtual() // 協(xié)程名稱 .name("fiber-1") // 協(xié)程名稱前綴 + 起始自增數(shù)字 => prefix + start,下一個(gè)創(chuàng)建的協(xié)程名稱就是prefix + (start + 1) // start > 0的情況下會(huì)覆蓋name屬性配置 .name("fiber-", 1L) // 是否啟用ThreadLocal .allowSetThreadLocals(false) // 是否啟用InheritableThreadLocal .inheritInheritableThreadLocals(false) // 設(shè)置調(diào)度器,Executor實(shí)例,也就是調(diào)度器是一個(gè)線程池,設(shè)置為NULL會(huì)使用VirtualThread.DEFAULT_SCHEDULER .scheduler(null) // 設(shè)置未捕獲異常處理器 .uncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { } }); // fiber-1 Thread firstFiber = virtualThreadBuilder.unstarted(() -> System.out.println("Hello Platform Virtual First")); // fiber-2 Thread secondFiber = virtualThreadBuilder.unstarted(() -> System.out.println("Hello Platform Virtual Second")); }
這里可以發(fā)現(xiàn)一點(diǎn),就是建造器是可以復(fù)用的。如果想用建造器創(chuàng)建同一批參數(shù)設(shè)置相同的線程或者協(xié)程,可以設(shè)置name(String prefix, long start)
方法,定義線程或者協(xié)程的名稱前綴和一個(gè)大于等于0
的數(shù)字,反復(fù)調(diào)用Builder#unstarted(Runnable task)
方法就能批量創(chuàng)建線程或者協(xié)程,名稱就設(shè)置為prefix + start
、prefix + (start + 1)
、prefix + (start + 2)
以此類推。協(xié)程創(chuàng)建基本就是這么簡(jiǎn)單,運(yùn)行的話直接調(diào)用start()
方法:
public class FiberSample2 { public static void main(String[] args) throws Exception { Thread.ofVirtual() .name("fiber-1") .allowSetThreadLocals(false) .inheritInheritableThreadLocals(false) .unstarted(() -> { Thread fiber = Thread.currentThread(); System.out.printf("[%s,daemon:%s,virtual:%s] - Hello World\n", fiber.getName(), fiber.isDaemon(), fiber.isVirtual()); }).start(); // 主線程休眠 Thread.sleep(Long.MAX_VALUE); } }
目前無法在主流IDE
編譯上面的類,所以只能使用該JDK
目錄下的工具編譯和運(yùn)行,具體如下:
# 執(zhí)行 - 當(dāng)前目錄I:\J-Projects\framework-source-code\fiber-sample\src\main\java (1)編譯:I:\Environment\Java\jdk-18-loom\bin\javac.exe I:\J-Projects\framework-source-code\fiber-sample\src\main\java\cn\throwx\fiber\sample\FiberSample2.java (2)執(zhí)行main方法:I:\Environment\Java\jdk-18-loom\bin\java.exe cn.throwx.fiber.sample.FiberSample2
這里也看出了一點(diǎn),所有的協(xié)程實(shí)例的daemon
標(biāo)識(shí)默認(rèn)為true
且不能修改。
小結(jié)
如果用嘗鮮的角度去使用Loom
項(xiàng)目,可以提前窺探JVM
開發(fā)者們是如何基于協(xié)程這個(gè)重大特性進(jìn)行開發(fā)的,這對(duì)于提高學(xué)習(xí)JDK
內(nèi)核代碼的興趣有不少幫助。從目前來看,對(duì)于協(xié)程的實(shí)現(xiàn)Loom
項(xiàng)目距離RELEASE
版本估計(jì)還有不少功能需要完善,包括新增API
的穩(wěn)定性,以及協(xié)程是否能夠移植到原有的JUC
類庫(kù)中使用(當(dāng)前的Loom-JDK-18+9
沒有對(duì)原來的線程池等類庫(kù)進(jìn)行修改)等問題需要解決,所以在保持關(guān)注的過程中靜心等待吧。
到此這篇關(guān)于Java協(xié)程編程之Loom項(xiàng)目嘗鮮的文章就介紹到這了,更多相關(guān)Java Loom項(xiàng)目?jī)?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring mvc @PathVariable綁定URI模板變量值方式
這篇文章主要介紹了spring mvc @PathVariable綁定URI模板變量值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11Java使用quartz實(shí)現(xiàn)定時(shí)任務(wù)示例詳解
這篇文章主要為大家介紹了Java使用quartz實(shí)現(xiàn)定時(shí)任務(wù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08Java實(shí)現(xiàn)線程同步的四種方式總結(jié)
Java線程同步屬于Java多線程與并發(fā)編程的核心點(diǎn),需要重點(diǎn)掌握,下面我就來詳解Java線程同步的4種主要的實(shí)現(xiàn)方式,需要的可以參考一下2022-09-09Java利用Geotools實(shí)現(xiàn)不同坐標(biāo)系之間坐標(biāo)轉(zhuǎn)換
GeoTools 是一個(gè)開源的 Java GIS 工具包,可利用它來開發(fā)符合標(biāo)準(zhǔn)的地理信息系統(tǒng)。本文將利用工具包Geotools實(shí)現(xiàn)不同坐標(biāo)系之間坐標(biāo)轉(zhuǎn)換,感興趣的可以了解一下2022-08-08