您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Java協程編程之Loom項目的示例分析,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
之前很長一段時間關注JDK
協程庫的開發進度,但是前一段時間比較忙很少去查看OpenJDK
官網的內容。Java
協程項目Loom
(因為項目還在開發階段,OpenJDK
給出的官網https://openjdk.java.net/projects/loom
中只有少量Loom
項目相關的信息)已經在2018
年之前立項,目前已經發布過基于JDK17
編譯和JDK18
編譯等早期版本,筆者在下載Loom
早期版本的時候只找到JDK18
編譯的版本:
下載入口在:https://jdk.java.net/loom
由于該JDK
版本過高,目前可以使用主流IDE
導入Loom-JDK-18+9
進行代碼高亮和語法提醒,暫時找不到方法進行編譯,暫時使用該JDK
執行目錄下的的javac
命令腳本進行編譯,使用java
命令腳本運行。
Loom - Fibers, Continuations and Tail-Calls for the JVM
Loom
項目的標題已經凸顯了引入的三大新特性:
Fibers
:幾年前看過當時的Loom
項目的測試代碼就是使用Fiber
這個API
(現在這個API
已經被移除),意為輕量級線程,即協程,又稱為輕量級用戶線程,很神奇的是在目前的JDK
中實際上稱為Virtual Thread
(虛擬線程)
Continuations
:直譯為"連續",實現上有點像閉包,參考不少資料,尚未準確理解其具體含義,感覺可以"粗暴"解讀為"程序接下來要執行什么"或者"下一個要執行的代碼塊"
Tail-Calls
:尾調用VM
級別支持
三個新特性不詳細展開,目前只是EA
版本,還存在修改的可能性,所以也沒必要詳細展開。
當前版本Loom
項目中協程使用并沒有引入一個新的公開的虛擬線程VirtualThread
類,雖然真的存在VirtualThread
,但這個類使用default
修飾符,隱藏在java.lang
包中,并且VirtualThread
是Thread
的子類。協程的創建API
位于Thread
類中:
使用此API
創建協程如下:
public static void main(String[] args) { Thread fiber = Thread.startVirtualThread(() -> System.out.println("Hello Fiber")); }
從當前的源碼可知:
VirtualThread
會通過Thread.currentThread()
獲取父線程的調度器,如果在main
方法運行,那么上面代碼中的協程實例的父線程就是main
線程
默認的調度器為系統創建的ForkJoinPool
實例(VirtualThread.DEFAULT_SCHEDULER
),輸入的Runnable
實例會被封裝為RunContinuation
,最終由調度器執行
對于timed unpark
(正在阻塞,等待喚醒)的協程,使用系統創建的ScheduledExecutorService
實例進行喚醒
這個靜態工廠方法創建完協程馬上運行,返回的是協程實例
如果按照上面的Thread.startVirtualThread()
方法去創建協程,顯然無法定義協程的名稱等屬性。Loom
項目為Thread
類引入了建造者模式,比較合理地解決了這個問題:
// 創建平臺線程建造器,對應于Thread實例 public static Builder.OfPlatform ofPlatform() { return new ThreadBuilders.PlatformThreadBuilder(); } // 創建虛擬線程建造器,對應于VirtualThread public static Builder.OfVirtual ofVirtual() { return new ThreadBuilders.VirtualThreadBuilder(); }
簡單說就是:
ofPlatform()
方法用于構建Thread
實例,這里的Platform Thread
(平臺線程)其實就是JDK1.0
引入的線程實例,普通的用戶線程
ofVirtual()
方法用于構建VirtualThread
實例,也就是構建協程實例
這兩個建造器實例的所有Setter
方法鏈展開如下:
public static void main(String[] args) { Thread.Builder.OfPlatform platformThreadBuilder = Thread.ofPlatform() // 是否守護線程 .daemon(true) // 線程組 .group(Thread.currentThread().getThreadGroup()) // 線程名稱 .name("thread-1") // 線程名稱前綴 + 起始自增數字 => prefix + start,下一個創建的線程名稱就是prefix + (start + 1) // start > 0的情況下會覆蓋name屬性配置 .name("thread-", 1L) // 是否啟用ThreadLocal .allowSetThreadLocals(false) // 是否啟用InheritableThreadLocal .inheritInheritableThreadLocals(false) // 設置優先級 .priority(100) // 設置線程棧深度 .stackSize(10) // 設置未捕獲異常處理器 .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() // 協程名稱 .name("fiber-1") // 協程名稱前綴 + 起始自增數字 => prefix + start,下一個創建的協程名稱就是prefix + (start + 1) // start > 0的情況下會覆蓋name屬性配置 .name("fiber-", 1L) // 是否啟用ThreadLocal .allowSetThreadLocals(false) // 是否啟用InheritableThreadLocal .inheritInheritableThreadLocals(false) // 設置調度器,Executor實例,也就是調度器是一個線程池,設置為NULL會使用VirtualThread.DEFAULT_SCHEDULER .scheduler(null) // 設置未捕獲異常處理器 .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")); }
這里可以發現一點,就是建造器是可以復用的。如果想用建造器創建同一批參數設置相同的線程或者協程,可以設置name(String prefix, long start)
方法,定義線程或者協程的名稱前綴和一個大于等于0
的數字,反復調用Builder#unstarted(Runnable task)
方法就能批量創建線程或者協程,名稱就設置為prefix + start
、prefix + (start + 1)
、prefix + (start + 2)
以此類推。協程創建基本就是這么簡單,運行的話直接調用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
目錄下的工具編譯和運行,具體如下:
# 執行 - 當前目錄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)執行main方法:I:\Environment\Java\jdk-18-loom\bin\java.exe cn.throwx.fiber.sample.FiberSample2
這里也看出了一點,所有的協程實例的daemon
標識默認為true
且不能修改。
關于“Java協程編程之Loom項目的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。