最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java?協(xié)程?Quasar詳解

 更新時間:2022年07月07日 08:36:26   作者:??碼農(nóng)參上????  
這篇文章主要介紹了Java?協(xié)程?Quasar詳解,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下

前言

在編程語言的這個圈子里,各種語言之間的對比似乎就一直就沒有停過,像什么古早時期的"PHP是世界上最好的語言"就不提了,最近我在摸魚的時候,看到不少文章都在說"Golang性能吊打Java"。作為一個寫了好幾年java的javaer,這我怎么能忍?于是在網(wǎng)上看了一些對比golang和java的文章,其中戳中java痛點、也是golang被吹上天的一條,就是對多線程并發(fā)的支持了。先看一段描述:

Go從語言層面原生支持并發(fā),并且使用簡單,Go語言中的并發(fā)基于輕量級線程Goroutine,創(chuàng)建成本很低,單個Go應(yīng)用也可以充分利用CPU多核,編寫高并發(fā)服務(wù)端軟件簡單,執(zhí)行性能好,很多情況下完全不需要考慮鎖機制以及由此帶來的各種問題。

看到這,我的心瞬間涼了大半截,真的是字字扎心。雖然說java里的JUC包已經(jīng)幫我們封裝好了很多并發(fā)工具,但實際高并發(fā)的環(huán)境中我們還要考慮到各種鎖的使用,以及服務(wù)器性能瓶頸、限流熔斷等非常多方面的問題。

再說回go,前面提到的這個goroutine究竟是什么東西?其實,輕量級線程goroutine也可以被稱為協(xié)程,得益于go中的調(diào)度器以及GMP模型,go程序會智能地將goroutine中的任務(wù)合理地分配給每個 CPU。

好了,其實上面說的這一大段我也不懂,都是向?qū)慻o的哥們兒請教來的,總之就是go的并發(fā)性能非常優(yōu)秀就是了。不過這都不是我們要說的重點,今天我們要討論的是如何在Java中使用協(xié)程。

協(xié)程是什么?

我們知道,線程在阻塞狀態(tài)和可運行狀態(tài)的切換,以及線程間的上下文切換都會造成性能的損耗。為了解決這些問題,引入?yún)f(xié)程coroutine這一概念,就像在一個進程中允許存在多個線程,在一個線程中,也可以存在多個協(xié)程。

那么,使用協(xié)程究竟有什么好處呢?

首先,執(zhí)行效率高。線程的切換由操作系統(tǒng)內(nèi)核執(zhí)行,消耗資源較多。而協(xié)程由程序控制,在用戶態(tài)執(zhí)行,不需要從用戶態(tài)切換到內(nèi)核態(tài),我們也可以理解為,協(xié)程是一種進程自身來調(diào)度任務(wù)的調(diào)度模式,因此協(xié)程間的切換開銷遠小于線程切換。

其次,節(jié)省資源。因為協(xié)程在本質(zhì)上是通過分時復用了一個單線程,因此能夠節(jié)省一定的資源。

類似于線程的五種狀態(tài)切換,協(xié)程間也存在狀態(tài)的切換,下面這張圖展示了協(xié)程調(diào)度器內(nèi)部任務(wù)的流轉(zhuǎn)。

綜合上面這些角度來看,和原生支持協(xié)程的go比起來,java在多線程并發(fā)上還真的是不堪一擊。但是,雖然在Java官方的jdk中不能直接使用協(xié)程,但是,有其他的開源框架借助動態(tài)修改字節(jié)碼的方式實現(xiàn)了協(xié)程,就比如我們接下來要學習的Quasar。

Quasar使用

Quasar是一個開源的Java協(xié)程框架,通過利用Java instrument技術(shù)對字節(jié)碼進行修改,使方法掛起前后可以保存和恢復jvm棧幀,方法內(nèi)部已執(zhí)行到的字節(jié)碼位置也通過增加狀態(tài)機的方式記錄,在下次恢復執(zhí)行可直接跳轉(zhuǎn)至最新位置。

Quasar項目最后更新時間為2018年,版本停留在0.8.0,但是我在直接使用這個版本時報了一個錯誤:

這個錯誤的大意就是這個class文件是使用的高版本jdk編譯的,所以你在低版本的jdk上當然無法運行了。這里major版本號54對應(yīng)的是jdk10,而我使用的是jdk8,無奈降級試了一下低版本,果然0.7.10可以使用:

<dependency>
    <groupId>co.paralleluniverse</groupId>
    <artifactId>quasar-core</artifactId>
    <version>0.7.10</version>
</dependency>

在我們做好準備工作后,下面就寫幾個例子來感受一下協(xié)程的魅力吧。

1、運行時間

下面我們模擬一個簡單的場景,假設(shè)我們有一個任務(wù),平均執(zhí)行時間為1秒,分別測試一下使用線程和協(xié)程并發(fā)執(zhí)行10000次需要消耗多少時間。

先通過線程進行調(diào)用,直接使用Executors線程池:

public static void main(String[] args) throws InterruptedException {
    CountDownLatch countDownLatch=new CountDownLatch(10000);
    long start = System.currentTimeMillis();
    ExecutorService executor= Executors.newCachedThreadPool();
    for (int i = 0; i < 10000; i++) {
        executor.submit(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();
        });
    }
    countDownLatch.await();
    long end = System.currentTimeMillis();
    System.out.println("Thread use:"+(end-start)+" ms");
}

查看運行時間:

好了,下面我們再用Quasar中的協(xié)程跑一下和上面相同的流程。這里我們要使用的是Quasar中的Fiber,它可以被翻譯為協(xié)程纖程,創(chuàng)建Fiber的類型主要可分為下面兩類:

public Fiber(String name, FiberScheduler scheduler, int stackSize, SuspendableRunnable target);
public Fiber(String name, FiberScheduler scheduler, int stackSize, SuspendableCallable<V> target);

Fiber中可以運行無返回值的SuspendableRunnable或有返回值的SuspendableCallable,看這個名字也知道區(qū)別就是java中的RunnableCallable的區(qū)別了。其余參數(shù)都可以省略,name為協(xié)程的名稱,scheduler是調(diào)度器,默認使用FiberForkJoinScheduler,stackSize指定用于保存fiber調(diào)用棧信息的stack大小。

在下面的代碼中,使用了Fiber.sleep()方法進行協(xié)程的休眠,和Thread.sleep()非常類似。

public static void main(String[] args) throws InterruptedException {
    CountDownLatch countDownLatch=new CountDownLatch(10000);
    long start = System.currentTimeMillis();

    for (int i = 0; i < 10000; i++) {
        new Fiber<>(new SuspendableRunnable(){
            @Override
            public Integer run() throws SuspendExecution, InterruptedException {
                Fiber.sleep(1000);
                countDownLatch.countDown();
            }
        }).start();
    }

    countDownLatch.await();
    long end = System.currentTimeMillis();
    System.out.println("Fiber use:"+(end-start)+" ms");
}

直接運行,報了一個警告:

QUASAR WARNING: Quasar Java Agent isn't running. If you're using another instrumentation method you can ignore this message; otherwise, please refer to the Getting Started section in the Quasar documentation.

還記得我們前面說過的Quasar生效的原理是基于Java instrument技術(shù)嗎,所以這里需要給它添加一個代理Agent。找到本地maven倉庫中已經(jīng)下好的jar包,在VM options中添加參數(shù):

-javaagent:E:\Apache\maven-repository\co\paralleluniverse\quasar-core\0.7.10\quasar-core-0.7.10.jar

這次運行時就沒有提示警告了,查看一下運行時間:

運行時間只有使用線程池時的一半多一點,確實能大大縮短程序的效率。

2、內(nèi)存占用

在測試完運行時間后,我們再來測試一下運行內(nèi)存占用的對比。通過下面代碼嘗試在本地啟動100萬個線程:

public static void main(String[] args) {
    for (int i = 0; i < 1000000; i++) {
        new Thread(() -> {
            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

本來以為會報OutOfMemoryError,但是沒想到的是我的電腦直接直接卡死了…而且不是一次,試了幾次都是以卡死只能重啟電腦而結(jié)束。好吧,我選擇放棄,那么下面再試試啟動100萬個Fiber協(xié)程。

public static void main(String[] args) throws Exception {
    CountDownLatch countDownLatch=new CountDownLatch(10000);
    for (int i = 0; i < 1000000; i++) {
        int finalI = i;
        new Fiber<>((SuspendableCallable<Integer>)()->{
            Fiber.sleep(100000);
            countDownLatch.countDown();
            return finalI;
        }).start();
    }
    countDownLatch.await();
    System.out.println("end");
}

程序能夠正常執(zhí)行結(jié)束,看樣子使用的內(nèi)存真的比線程少很多。上面我故意使每個協(xié)程結(jié)束的時間拖得很長,這樣我們就可以在運行過程中使用Java VisualVM查看內(nèi)存的占用情況了:

可以看到在使用Fiber的情況下只使用了1G多一點的內(nèi)存,平均到100萬個協(xié)程上也就是說每個Fiber只占用了1Kb左右的內(nèi)存空間,和Thread線程比起來真的是非常的輕量級。

從上面這張圖中我們也可以看到,運行了非常多的ForkJoinPool,它們又起到了什么作用呢?我們在前面說過,協(xié)程是由程序控制在用戶態(tài)進行切換,而Quasar中的調(diào)度器就使用了一個或多個ForkJoinPool來完成對Fiber的調(diào)度。

3、原理與應(yīng)用

這里簡單介紹一下Quasar的原理,在編譯時框架會對代碼進行掃描,如果方法帶有@Suspendable注解,或拋出了SuspendExecution,或在配置文件META-INF/suspendables中指定該方法,那么Quasar就會修改生成的字節(jié)碼,在park掛起方法的前后,插入一些字節(jié)碼。

這些字節(jié)碼會記錄此時協(xié)程的執(zhí)行狀態(tài),例如相關(guān)的局部變量與操作數(shù)棧,然后通過拋出異常的方式將cpu的控制權(quán)從當前協(xié)程交回到控制器,此時控制器可以再調(diào)度另外一個協(xié)程運行,并通過之前插入的那些字節(jié)碼恢復當前協(xié)程的執(zhí)行狀態(tài),使程序能繼續(xù)正常執(zhí)行。

回頭看一下前面例子中的SuspendableRunnableSuspendableCallable,它們的run方法上都拋出了SuspendExecution,其實這并不是一個真正的異常,僅作為識別掛起方法的聲明,在實際運行中不會拋出。當我們創(chuàng)建了一個Fiber,并在其中調(diào)用了其他方法時,如果想要Quasar的調(diào)度器能夠介入,那么必須在使用時層層拋出這個異?;蛱砑幼⒔狻?/p>

看一下簡單的代碼書寫的示例:

public void request(){
    new Fiber<>(new SuspendableRunnable() {
        @Override
        public void run() throws SuspendExecution, InterruptedException {
            String content = sendRequest();
            System.out.println(content);
        }
    }).start();
}
private String sendRequest() throws SuspendExecution {
    return realSendRequest();
}
private String realSendRequest() throws SuspendExecution{
    HttpResponse response = HttpRequest.get("http://127.0.0.1:6879/name").execute();
    String content = response.body();
    return content;
}

需要注意的是,如果在方法內(nèi)部已經(jīng)通過try/catch的方式捕獲了Exception,也應(yīng)該再次手動拋出這個SuspendExecution異常。

總結(jié)

本文介紹了Quasar框架的簡單使用,其具體的實現(xiàn)原理比較復雜,暫時就不在這里進行討論,后面打算單獨拎出來進行分析。另外,目前已經(jīng)有不少其他的框架中已經(jīng)集成了Quasar,例如同樣是Parallel Universe下的Comsat項目,能夠提供了HTTP和DB訪問等功能。

雖然現(xiàn)在想要在Java中使用協(xié)程還只能使用這樣的第三方的框架,但是也不必灰心,在OpenJDK 16中已經(jīng)加入了一個名為Project Loom的項目, 在OpenJDK Wiki上可以看到對它的介紹,它將使用Fiber輕量級用戶模式線程,從jvm層面對多線程技術(shù)進行徹底的改變,使用新的編程模型,使輕量級線程的并發(fā)也能夠適用于高吞吐量的業(yè)務(wù)場景。

到此這篇關(guān)于Java 協(xié)程 Quasar詳解的文章就介紹到這了,更多相關(guān)Java Quasar內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java File類的基本使用方法總結(jié)

    java File類的基本使用方法總結(jié)

    這篇文章主要介紹了java File類的基本使用方法總結(jié),為大家分享了java實現(xiàn)上傳代碼,感興趣的小伙伴們可以參考一下
    2016-04-04
  • Java中使用Closeable接口自動關(guān)閉資源詳解

    Java中使用Closeable接口自動關(guān)閉資源詳解

    這篇文章主要介紹了Java中使用Closeable接口自動關(guān)閉資源詳解,Closeable接口繼承于AutoCloseable,主要的作用就是自動的關(guān)閉資源,其中close()方法是關(guān)閉流并且釋放與其相關(guān)的任何方法,如果流已被關(guān)閉,那么調(diào)用此方法沒有效果,需要的朋友可以參考下
    2023-12-12
  • 如何設(shè)置IDEA遠程連接服務(wù)器開發(fā)環(huán)境并結(jié)合cpolar實現(xiàn)ssh遠程開發(fā)(最新推薦)

    如何設(shè)置IDEA遠程連接服務(wù)器開發(fā)環(huán)境并結(jié)合cpolar實現(xiàn)ssh遠程開發(fā)(最新推薦)

    本文主要介紹如何在IDEA中設(shè)置遠程連接服務(wù)器開發(fā)環(huán)境,并結(jié)合Cpolar內(nèi)網(wǎng)穿透工具實現(xiàn)無公網(wǎng)遠程連接,然后實現(xiàn)遠程Linux環(huán)境進行開發(fā),感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • 詳細講述Java中的對象轉(zhuǎn)型

    詳細講述Java中的對象轉(zhuǎn)型

    在本篇文章里我們給大家詳細分享了關(guān)于Java中的對象轉(zhuǎn)型的知識點內(nèi)容,有興趣的朋友們學習下。
    2018-10-10
  • mybatis?log4j2打印sql+日志實例代碼

    mybatis?log4j2打印sql+日志實例代碼

    在學習mybatis的時候,如果用log4j2來協(xié)助查看調(diào)試信息,則會大大提高學習的效率,加快debug速度,下面這篇文章主要給大家介紹了關(guān)于mybatis?log4j2打印sql+日志的相關(guān)資料,需要的朋友可以參考下
    2022-08-08
  • 教你怎么用Java完成人民幣大寫轉(zhuǎn)化

    教你怎么用Java完成人民幣大寫轉(zhuǎn)化

    這篇文章主要介紹了教你怎么用Java完成人民幣大寫轉(zhuǎn)化,文中有非常詳細的代碼示例,對正在學習java的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-04-04
  • SpringSecurity身份認證原理解析

    SpringSecurity身份認證原理解析

    這篇文章主要介紹了SpringSecurity身份認證原理解析,身份認證時用戶名和密碼被過濾器獲取到,封裝成 Authentication ,通常情況下是 UsernamePasswordAuthenticationToken 這個實現(xiàn)類,需要的朋友可以參考下
    2023-09-09
  • 如何在IDEA啟動多個Spring Boot工程實例(圖文)

    如何在IDEA啟動多個Spring Boot工程實例(圖文)

    這篇文章主要介紹了如何在IDEA啟動多個Spring Boot工程實例(圖文),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-09-09
  • Java基本數(shù)據(jù)類型與類型轉(zhuǎn)換實例分析

    Java基本數(shù)據(jù)類型與類型轉(zhuǎn)換實例分析

    這篇文章主要介紹了Java基本數(shù)據(jù)類型與類型轉(zhuǎn)換,結(jié)合實例形式分析了Java基本數(shù)據(jù)類型分類、用法,類型轉(zhuǎn)換及相關(guān)操作注意事項,需要的朋友可以參考下
    2020-04-04
  • @FeignClient?實現(xiàn)簡便http請求封裝方式

    @FeignClient?實現(xiàn)簡便http請求封裝方式

    這篇文章主要介紹了@FeignClient?實現(xiàn)簡便http請求封裝方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03

最新評論

梅河口市| 安吉县| 江川县| 原平市| 南丰县| 鄢陵县| 花莲市| 铜山县| 南京市| 长阳| 张家界市| 和顺县| 阿尔山市| 武功县| 凌源市| 尤溪县| 龙胜| 邛崃市| 安溪县| 西宁市| 泾川县| 兴安县| 凭祥市| 前郭尔| 故城县| 徐州市| 大同县| 辛集市| 安新县| 靖边县| 旅游| 斗六市| 和田市| 吉林省| 潞城市| 内丘县| 巴东县| 辽阳市| 安平县| 永宁县| 麻阳|