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中的Runnable
和Callable
的區別了。其餘引數都可以省略,name
為協程的名稱,scheduler
是排程器,預設使用FiberForkJoinScheduler
,stackSize
指定用於儲存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