电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

Java 實現協程的方法

瀏覽:41日期:2022-08-22 18:28:32

協程(Coroutine)這個詞其實有很多叫法,比如有的人喜歡稱為纖程(Fiber),或者綠色線程(GreenThread)。其實究其本質,對于協程最直觀的解釋是線程的線程。雖然讀上去有點拗口,但本質上就是這樣。

協程的核心在于調度那塊由他來負責解決,遇到阻塞操作,立刻放棄掉,并且記錄當前棧上的數據,阻塞完后立刻再找一個線程恢復棧并把阻塞的結果放到這個線程上去跑,這樣看上去好像跟寫同步代碼沒有任何差別,這整個流程可以稱為coroutine,而跑在由coroutine負責調度的線程稱為Fiber。

java協程的實現

早期,在JVM上實現協程一般會使用kilim,不過這個工具已經很久不更新了,現在常用的工具是Quasar,而本文章會全部基于Quasar來介紹。

下面嘗試通過Quasar來實現類似于go語言的coroutine以及channel。

為了能有明確的對比,這里先用go語言實現一個對于10以內自然數分別求平方的例子。

func counter(out chan<- int) { for x := 0; x < 10; x++ { out <- x } close(out)}func squarer(out chan<- int, in <-chan int) { for v := range in { out <- v * v } close(out)}func printer(in <-chan int) { for v := range in { fmt.Println(v) }}func main() { //定義兩個int類型的channel naturals := make(chan int) squares := make(chan int) //產生兩個Fiber,用go關鍵字 go counter(naturals) go squarer(squares, naturals) //獲取計算結果 printer(squares)}

上面這個例子,通過channel兩解耦兩邊的數據共享。對于這個channel,大家可以理解為Java里的SynchronousQueue。下面我直接上Quasar版JAVA代碼的,幾乎可以原封不動的復制go語言的代碼。

public class Example { private static void printer(Channel<Integer> in) throws SuspendExecution, InterruptedException { Integer v; while ((v = in.receive()) != null) { System.out.println(v); } } public static void main(String[] args) throws ExecutionException, InterruptedException, SuspendExecution { //定義兩個Channel Channel<Integer> naturals = Channels.newChannel(-1); Channel<Integer> squares = Channels.newChannel(-1); //運行兩個Fiber實現. new Fiber(() -> { for (int i = 0; i < 10; i++) naturals.send(i); naturals.close(); }).start(); new Fiber(() -> { Integer v; while ((v = naturals.receive()) != null) squares.send(v * v); squares.close(); }).start(); printer(squares); }}

兩者對比,看上去Java似好像更復雜些,沒辦法這就是Java的風格,而且這還是通過第三方的庫來實現的。

說到這里各位肯定對Fiber很好奇了。也許你會表示懷疑Fiber是不是如上面所描述的那樣,下面我們嘗試用Quasar建立一百萬個Fiber,看看內存占用多少,我先嘗試了創建百萬個Thread。

for (int i = 0; i < 1_000_000; i++) { new Thread(() -> { try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } }).start();}

很不幸,直接報Exception in thread 'main' java.lang.OutOfMemoryError: unable to create new native thread,這是情理之中的。下面是通過Quasar建立百萬個Fiber。

public static void main(String[] args) throws ExecutionException, InterruptedException, SuspendExecution { int FiberNumber = 1_000_000; CountDownLatch latch = new CountDownLatch(1); AtomicInteger counter = new AtomicInteger(0); for (int i = 0; i < FiberNumber; i++) { new Fiber(() -> { counter.incrementAndGet(); if (counter.get() == FiberNumber) { System.out.println('done'); } Strand.sleep(1000000); }).start(); } latch.await();}

我這里加了latch,阻止程序跑完就關閉,Strand.sleep其實跟Thread.sleep一樣,只是這里針對的是Fiber。

最終控制臺是可以輸出done的,說明程序已經創建了百萬個Fiber,設置Sleep是為了讓Fiber一直運行,從而方便計算內存占用。官方宣稱一個空閑的Fiber大約占用400Byte,那這里應該是占用400MB堆內存,但是這里通過jmap -heap pid顯示大約占用了1000MB,也就是說一個Fiber占用1KB。

Quasar是怎么實現Fiber的

其實Quasar實現的coroutine的方式與Go語言很像,只不過前者是使用框架來實現,而go語言則是語言內置的功能。

不過如果你熟悉了Go語言的調度機制的話,那么對于Quasar的調度機制就會好理解很多了,因為兩者有很多相似之處。

Quasar里的Fiber其實是一個continuation,他可以被Quasar定義的scheduler調度,一個continuation記錄著運行實例的狀態,而且會被隨時中斷,并且也會隨后在他被中斷的地方恢復。

Quasar其實是通過修改bytecode來達到這個目的,所以運行Quasar程序的時候,你需要先通過java-agent在運行時修改你的代碼,當然也可以在編譯期間這么干。go語言的內置了自己的調度器,而Quasar則是默認使用ForkJoinPool這個具有work-stealing功能的線程池來當調度器。work-stealing非常重要,因為你不清楚哪個Fiber會先執行完,而work-stealing可以動態的從其他的等等隊列偷一個context過來,這樣可以最大化使用CPU資源。

那這里你會問了,Quasar怎么知道修改哪些字節碼呢,其實也很簡單,Quasar會通過java-agent在運行時掃描哪些方法是可以中斷的,同時會在方法被調用前和調度后的方法內插入一些continuation邏輯,如果你在方法上定義了@Suspendable注解,那Quasar會對調用該注解的方法做類似下面的事情。

這里假設你在方法f上定義了@Suspendable,同時去調用了有同樣注解的方法g,那么所有調用f的方法會插入一些字節碼,這些字節碼的邏輯就是記錄當前Fiber棧上的狀態,以便在未來可以動態的恢復。(Fiber類似線程也有自己的棧)。在suspendable方法鏈內Fiber的父類會調用Fiber.park,這樣會拋出SuspendExecution異常,從而來停止線程的運行,好讓Quasar的調度器執行調度。這里的SuspendExecution會被Fiber自己捕獲,業務層面上不應該捕獲到。如果Fiber被喚醒了(調度器層面會去調用Fiber.unpark),那么f會在被中斷的地方重新被調用(這里Fiber會知道自己在哪里被中斷),同時會把g的調用結果(g會return結果)插入到f的恢復點,這樣看上去就好像g的return是f的local variables了,從而避免了callback嵌套。

上面說了一大堆,其實簡單點來講就是,想辦法讓運行中的線程棧停下來,然后讓Quasar的調度器介入。

JVM線程中斷的條件有兩個:

1、拋異常

2、return。

而在Quasar中,一般就是通過拋異常的方式來達到的,所以你會看到上面的代碼會拋出SuspendExecution。但是如果你真捕獲到這個異常,那就說明有問題了,所以一般會這么寫。

@Suspendablepublic int f() { try { // do some stuff return g() * 2; } catch(SuspendExecution s) { //這里不應該捕獲到異常. throw new AssertionError(s); }}

以上就是Java 實現協程的方法的詳細內容,更多關于Java 實現協程的資料請關注好吧啦網其它相關文章!

標簽: Java
相關文章:
主站蜘蛛池模板: 西门子伺服控制器维修-伺服驱动放大器-828D数控机床维修-上海涌迪 | 上海道勤塑化有限公司| 能耗监测系统-节能监测系统-能源管理系统-三水智能化 | U拓留学雅思一站式服务中心_留学申请_雅思托福培训 | CNC机加工-数控加工-精密零件加工-ISO认证厂家-鑫创盟 | 工业用品一站式采购平台|南创工品汇-官网|广州南创 | 密集架|电动密集架|移动密集架|黑龙江档案密集架-大量现货厂家销售 | 上海恒驭仪器有限公司-实验室平板硫化机-小型平板硫化机-全自动平板硫化机 | 山东锐智科电检测仪器有限公司_超声波测厚仪,涂层测厚仪,里氏硬度计,电火花检漏仪,地下管线探测仪 | 杜康白酒加盟_杜康酒代理_杜康酒招商加盟官网_杜康酒厂加盟总代理—杜康酒神全国运营中心 | 恒温槽_恒温水槽_恒温水浴槽-上海方瑞仪器有限公司 | 打包箱房_集成房屋-山东佳一集成房屋有限公司 | CNC机加工-数控加工-精密零件加工-ISO认证厂家-鑫创盟 | 江门流水线|江门工作台|江门市伟涛行工业设备有限公司 | 重庆网站建设,重庆网站设计,重庆网站制作,重庆seo,重庆做网站,重庆seo,重庆公众号运营,重庆小程序开发 | 卫生纸复卷机|抽纸机|卫生纸加工设备|做卫生纸机器|小型卫生纸加工需要什么设备|卫生纸机器设备多少钱一台|许昌恒源纸品机械有限公司 | 真空搅拌机-行星搅拌机-双行星动力混合机-广州市番禺区源创化工设备厂 | 菏泽知彼网络科技有限公司 | 哈尔滨发电机,黑龙江柴油发电机组-北方星光 | 电镀电源整流器_高频电解电源_单脉双脉冲电源 - 东阳市旭东电子科技 | 岛津二手液相色谱仪,岛津10A液相,安捷伦二手液相,安捷伦1100液相-杭州森尼欧科学仪器有限公司 | 烟台条码打印机_烟台条码扫描器_烟台碳带_烟台数据采集终端_烟台斑马打印机-金鹏电子-金鹏电子 | 精密模具制造,注塑加工,吹塑和吹瓶加工,EPS泡沫包装生产 - 济南兴田塑胶有限公司 | 活性炭厂家-蜂窝活性炭-粉状/柱状/果壳/椰壳活性炭-大千净化-活性炭 | 理化生实验室设备,吊装实验室设备,顶装实验室设备,实验室成套设备厂家,校园功能室设备,智慧书法教室方案 - 东莞市惠森教学设备有限公司 | 电动卫生级调节阀,电动防爆球阀,电动软密封蝶阀,气动高压球阀,气动对夹蝶阀,气动V型调节球阀-上海川沪阀门有限公司 | 球盟会·(中国)官方网站 | 数码听觉统合训练系统-儿童感觉-早期言语评估与训练系统-北京鑫泰盛世科技发展有限公司 | 吉祥新世纪铝塑板_生产铝塑板厂家_铝塑板生产厂家_临沂市兴达铝塑装饰材料有限公司 | 睿婕轻钢别墅_钢结构别墅_厂家设计施工报价| 苏州柯瑞德货架-仓库自动化改造解决方案 | 电动卫生级调节阀,电动防爆球阀,电动软密封蝶阀,气动高压球阀,气动对夹蝶阀,气动V型调节球阀-上海川沪阀门有限公司 | 首页-瓜尔胶系列-化工单体系列-油田压裂助剂-瓜尔胶厂家-山东广浦生物科技有限公司 | 山东聚盛新型材料有限公司-纳米防腐隔热彩铝板和纳米防腐隔热板以及钛锡板、PVDF氟膜板供应商 | 河南档案架,档案密集架,手动密集架,河南密集架批发/报价 | 天津试验仪器-电液伺服万能材料试验机,恒温恒湿标准养护箱,水泥恒应力压力试验机-天津鑫高伟业科技有限公司 | 档案密集架,移动密集架,手摇式密集架,吉林档案密集架-厂家直销★价格公道★质量保证 | 电解抛光加工_不锈钢电解抛光_常州安谱金属制品有限公司 | KBX-220倾斜开关|KBW-220P/L跑偏开关|拉绳开关|DHJY-I隔爆打滑开关|溜槽堵塞开关|欠速开关|声光报警器-山东卓信有限公司 | 外贮压-柜式-悬挂式-七氟丙烷-灭火器-灭火系统-药剂-价格-厂家-IG541-混合气体-贮压-非贮压-超细干粉-自动-灭火装置-气体灭火设备-探火管灭火厂家-东莞汇建消防科技有限公司 | 酒万铺-酒水招商-酒水代理 |