java協(xié)程框架quasar和kotlin中的協(xié)程對(duì)比分析
前言
早就聽(tīng)說(shuō)Go語(yǔ)言開(kāi)發(fā)的服務(wù)不用任何架構(gòu)優(yōu)化,就可以輕松實(shí)現(xiàn)百萬(wàn)級(jí)別的qps。這得益于Go語(yǔ)言級(jí)別的協(xié)程的處理效率。協(xié)程不同于線(xiàn)程,線(xiàn)程是操作系統(tǒng)級(jí)別的資源,創(chuàng)建線(xiàn)程,調(diào)度線(xiàn)程,銷(xiāo)毀線(xiàn)程都是重量級(jí)別的操作。而且線(xiàn)程的資源有限,在java中大量的不加限制的創(chuàng)建線(xiàn)程非常容易將系統(tǒng)搞垮。接下來(lái)要分享的這個(gè)開(kāi)源項(xiàng)目,正是解決了在java中只能使用多線(xiàn)程模型開(kāi)發(fā)高并發(fā)應(yīng)用的窘境,使得java也能像Go語(yǔ)言那樣使用協(xié)程的語(yǔ)義開(kāi)發(fā)了。
- quasar項(xiàng)目地址:https://github.com/puniverse/quasar
- quasar周邊項(xiàng)目地址:https://github.com/puniverse/comsat
快速體驗(yàn)
添加依賴(lài)
<dependency> <groupId>co.paralleluniverse</groupId> <artifactId>quasar-core</artifactId> <version>0.7.10</version> </dependency>
注意:目前quasar最高的版本是0.8.0,但是最高版本的只支持jdk11以上
添加java agent
quasar的實(shí)現(xiàn)原理是在java加載class前,通過(guò)jdk的instrument機(jī)制使用asm來(lái)修改目標(biāo)class的字節(jié)碼來(lái)實(shí)現(xiàn)的,他標(biāo)記了協(xié)程代碼的起始和結(jié)束的位置,以及方法需要暫停的位置,每個(gè)協(xié)程任務(wù)統(tǒng)一由FiberScheduler去調(diào)度,內(nèi)部維護(hù)了一個(gè)或多個(gè)ForkJoinPool實(shí)例。所以,在運(yùn)行應(yīng)用前,需要配置好quasar-core的java agent地址,在vm參數(shù)上加上如下腳本即可:
-javaagent:D:\.m2\repository\co\paralleluniverse\quasar-core\0.7.10\quasar-core-0.7.10.jar
線(xiàn)程VS協(xié)程
下面模擬調(diào)用某個(gè)遠(yuǎn)程的服務(wù),假設(shè)遠(yuǎn)程服務(wù)處理耗時(shí)需要1S,這里使用執(zhí)行阻塞1S來(lái)模擬,分別看多線(xiàn)程模型和協(xié)程模型調(diào)用這個(gè)服務(wù)10000次所需的耗時(shí)
協(xié)程代碼
public static void main(String[] args) throws Exception{ CountDownLatch count = new CountDownLatch(10000); StopWatch stopWatch = new StopWatch();stopWatch.start(); IntStream.range(0,10000).forEach(i-> new Fiber() { @Override protected String run() throws SuspendExecution, InterruptedException { Strand.sleep(1000 ); count.countDown(); return "aa"; } }.start()); count.await();stopWatch.stop(); System.out.println("結(jié)束了: " + stopWatch.prettyPrint()); }
耗時(shí)情況:
多線(xiàn)程代碼
public static void main(String[] args) throws Exception{ CountDownLatch count = new CountDownLatch(10000); StopWatch stopWatch = new StopWatch();stopWatch.start(); ExecutorService executorService = Executors.newCachedThreadPool(); IntStream.range(0,10000).forEach(i-> executorService.submit(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException ex) { } count.countDown(); })); count.await();stopWatch.stop(); System.out.println("結(jié)束了: " + stopWatch.prettyPrint()); }
耗時(shí)情況
協(xié)程完勝
可以看到上面的結(jié)果,在對(duì)比訪(fǎng)問(wèn)一個(gè)耗時(shí)1s的服務(wù)10000次時(shí),協(xié)程只需要2秒多,而多線(xiàn)程模型需要4秒多,時(shí)效相差了一倍。而且上面多線(xiàn)程編程時(shí),并沒(méi)有指定線(xiàn)程池的大小,在實(shí)際開(kāi)發(fā)中是絕不允許的。一般我們會(huì)設(shè)置一個(gè)固定大小的線(xiàn)程池,因?yàn)榫€(xiàn)程資源是寶貴,線(xiàn)程多了費(fèi)內(nèi)存還會(huì)帶來(lái)線(xiàn)程切換的開(kāi)銷(xiāo)。上面的場(chǎng)景在設(shè)置200個(gè)固定大小線(xiàn)程池時(shí)。結(jié)果也是可預(yù)見(jiàn)的達(dá)到了50多秒。這個(gè)結(jié)果足以證明協(xié)程編程ko線(xiàn)程編程了。而且在qps越大時(shí),線(xiàn)程處理的效率和協(xié)程的差距就約明顯,縮小差距的唯一方式就是增加線(xiàn)程數(shù),而這帶來(lái)的影響就是內(nèi)存消耗激增。而反觀協(xié)程,基于固定的幾個(gè)線(xiàn)程調(diào)度,可以輕松實(shí)現(xiàn)百萬(wàn)級(jí)的協(xié)程處理,而且內(nèi)存穩(wěn)穩(wěn)的。
后記
最后,博主以為Quasar只是一個(gè)框架層面的東西,所以就又去看了下同樣是jvm語(yǔ)言的kotlin的協(xié)程。他的語(yǔ)言更簡(jiǎn)潔,可以直接和java混合使用。跑上面這種實(shí)例只需要1秒多。
fun main() { val count = CountDownLatch(10000) val stopWatch = StopWatch() stopWatch.start() IntStream.range(0,10000).forEach { GlobalScope.launch { delay(1000L) println(Thread.currentThread().name + "->"+ it) count.countDown() } } count.await() stopWatch.stop() println("結(jié)束了: " + stopWatch.prettyPrint()) }
當(dāng)博主看到這個(gè)結(jié)果的時(shí)候,有種震驚的趕腳,kotlin的同步模型牛逼呀,瞬時(shí)感覺(jué)到發(fā)現(xiàn)了java里的騷操作了,可以使用kotlin的協(xié)程來(lái)代替java中的多線(xiàn)程操作。因?yàn)樗麄儍蓚€(gè)混合開(kāi)發(fā)毫無(wú)壓力。如果行的通,那就太爽了。所以就有下面這個(gè)kotlin協(xié)程實(shí)現(xiàn)的代碼:
@Service class KotlinAsyncService(private val weatherService: GetWeatherService,private val demoApplication: DemoApplication){ val weatherUrl = "http://localhost:8080/demo/mockWeatherApi?city=" fun getHuNanWeather(): JSONObject{ val result = JSONObject() val count = CountDownLatch(demoApplication.weatherContext.size) for (city in demoApplication.weatherContext){ val url = weatherUrl + city.key GlobalScope.launch { result[city.key.toString()] = weatherService.get(url) count.countDown() } } count.await() return result } }
現(xiàn)實(shí)是,當(dāng)我使用協(xié)程替換掉我java多線(xiàn)程寫(xiě)的一個(gè)多線(xiàn)程匯聚多個(gè)http接口的結(jié)果的接口時(shí),通過(guò)ab壓測(cè)他們兩個(gè)的性能并沒(méi)有很大的變化,最后了解到主要原因是這個(gè)時(shí)候,在協(xié)程里發(fā)起一個(gè)http的請(qǐng)求時(shí),涉及到操作系統(tǒng)層面的socket io操作,io操作是阻塞的,協(xié)程的并發(fā)也就變成了調(diào)度協(xié)程的幾個(gè)線(xiàn)程的并發(fā)了。而且當(dāng)我把同樣的代碼放到Quasar中的時(shí)候,Quasar直接拋io異常了,說(shuō)明Quasar還并不能輕松支持這個(gè)場(chǎng)景。那為什么上面的測(cè)試結(jié)果差距這么大呢,是因?yàn)槲义e(cuò)誤的把協(xié)程實(shí)現(xiàn)里的阻塞等同于線(xiàn)程的阻塞。協(xié)程里的delay掛起函數(shù),會(huì)立馬釋放線(xiàn)程到線(xiàn)程池,但是當(dāng)真正的io阻塞的時(shí)候也就和真正的線(xiàn)程sleep一樣了,并沒(méi)有釋放當(dāng)前的線(xiàn)程。所以這些對(duì)比都沒(méi)有太大的意義
以上就是java協(xié)程框架quasar和kotlin中的協(xié)程對(duì)比分析的詳細(xì)內(nèi)容,更多關(guān)于java框架quasar和kotlin協(xié)程對(duì)比的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java異常處理機(jī)制示例(java拋出異常、捕獲、斷言)
這篇文章主要介紹了java異常處理機(jī)制示例(java拋出異常、捕獲、斷言),需要的朋友可以參考下2014-05-05SSH框架網(wǎng)上商城項(xiàng)目第6戰(zhàn)之基于DataGrid的數(shù)據(jù)顯示
SSH框架網(wǎng)上商城項(xiàng)目第6戰(zhàn)之基于DataGrid的數(shù)據(jù)顯示,提供了豐富的選擇、排序、分組和編輯數(shù)據(jù)的功能支持,感興趣的小伙伴們可以參考一下2016-05-05解決poi導(dǎo)出excel無(wú)法打開(kāi)文件的問(wèn)題
這篇文章主要介紹了解決poi導(dǎo)出excel無(wú)法打開(kāi)文件的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12詳解spring security四種實(shí)現(xiàn)方式
這篇文章主要介紹了詳解spring security四種實(shí)現(xiàn)方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11IDEA創(chuàng)建Maven一直爆紅無(wú)法下載的問(wèn)題解決辦法
這篇文章主要介紹了關(guān)于IDEA創(chuàng)建Maven一直爆紅無(wú)法下載的問(wèn)題的解決辦法,文中圖文結(jié)合的方式給大家講解的非常詳細(xì),對(duì)大家解決辦法非常有用,需要的朋友可以參考下2024-06-06推薦兩款java開(kāi)發(fā)實(shí)用工具 hutool 和 lombok
通過(guò)本文給大家推薦兩款java開(kāi)發(fā)實(shí)用工具 hutool 和 lombok,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-04-04SpringBoot使用jasypt實(shí)現(xiàn)數(shù)據(jù)庫(kù)信息脫敏的方法詳解
這篇文章主要介紹了SpringBoot使用jasypt實(shí)現(xiàn)數(shù)據(jù)庫(kù)信息的脫敏,以此來(lái)保護(hù)數(shù)據(jù)庫(kù)的用戶(hù)名username和密碼password(容易上手,詳細(xì)),文中有詳細(xì)的圖文講解和代碼示例供大家參考,需要的朋友可以參考下2024-06-06linux用java -jar啟動(dòng)jar包緩慢的問(wèn)題
這篇文章主要介紹了linux用java -jar啟動(dòng)jar包緩慢的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,2023-09-09Java項(xiàng)目實(shí)現(xiàn)五子棋小游戲
這篇文章主要為大家詳細(xì)介紹了Java項(xiàng)目實(shí)現(xiàn)五子棋小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05