1. 程式人生 > 其它 >Java 協程 Quasar

Java 協程 Quasar

執行緒在阻塞狀態和可執行狀態的切換,以及執行緒間的上下文切換都會造成效能的損耗。為了解決這些問題,引入協程coroutine這一概念,就像在一個程序中允許存在多個執行緒,在一個執行緒中,也可以存在多個協程。

 

使用協程究竟有什麼好處呢?

首先,執行效率高。執行緒的切換由作業系統核心執行,消耗資源較多。而協程由程式控制,在使用者態執行,不需要從使用者態切換到核心態,我們也可以理解為,協程是一種程序自身來排程任務的排程模式,因此協程間的切換開銷遠小於執行緒切換。

其次,節省資源。因為協程在本質上是通過分時複用了一個單執行緒,因此能夠節省一定的資源。

 

雖然在Java官方的jdk中不能直接使用協程,但是,有其他的開源框架藉助動態修改位元組碼的方式實現了協程,比如Quasar。

 

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

  

下面我們模擬一個簡單的場景,假設我們有一個任務,平均執行時間為1秒,分別測試一下使用執行緒和協程併發執行10000次需要消耗多少時間。

先通過執行緒進行呼叫,直接使用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中的協程跑一下和上面相同的流程。這裡我們要使用的是Quasar中的Fiber,它可以被翻譯為協程纖程,建立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,看這個名字也知道區別就是java中的RunnableCallable的區別了。其餘引數都可以省略,name為協程的名稱,scheduler是排程器,預設使用FiberForkJoinSchedulerstackSize指定用於儲存fiber呼叫棧資訊的stack大小。

在下面的程式碼中,使用了Fiber.sleep()方法進行協程的休眠,和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 void 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技術嗎,所以這裡需要給它新增一個代理Agent。找到本地maven倉庫中已經下好的jar包,在VM options中新增引數:

-javaagent:C:\Users\tl19638\.m2\repository\co\paralleluniverse\quasar-core\0.7.10\quasar-core-0.7.10.jar

  執行後時間只有使用執行緒池時的一半多一點,確實能大大縮短程式的效率。

 

文章參考:https://mp.weixin.qq.com/s/U1IlB_fv2BMAs5r74kDdNg