Java 协程 Quasar

线程在阻塞状态和可运行状态的切换,以及线程间的上下文切换都会造成性能的损耗。为了解决这些问题,引入协程coroutine这一概念,就像在一个进程中允许存在多个线程,在一个线程中,也可以存在多个协程。

 

使用协程究竟有什么好处呢?

首先,执行效率高。线程的切换由操作系统内核执行,消耗资源较多。而协程由程序控制,在用户态执行,不需要从用户态切换到内核态,我们也可以理解为,协程是一种进程自身来调度任务的调度模式,因此协程间的切换开销远小于线程切换。

其次,节省资源。因为协程在本质上是通过分时复用了一个单线程,因此能够节省一定的资源。

 

虽然在Java官方的jdk中不能直接使用协程,但是,有其他的开源框架借助动态修改字节码的方式实现了协程,比如Quasar。

 

1
2
3
4
5
<dependency>
    <groupId>co.paralleluniverse</groupId>
    <artifactId>quasar-core</artifactId>
    <version>0.7.10</version>
</dependency>

  

下面我们模拟一个简单的场景,假设我们有一个任务,平均执行时间为1秒,分别测试一下使用线程和协程并发执行10000次需要消耗多少时间。

先通过线程进行调用,直接使用Executors线程池:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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的类型主要可分为下面两类:

1
2
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()非常类似。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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");
}

直接运行,报了一个警告:

1
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中添加参数:

1
-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

posted @   草木物语  阅读(368)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
历史上的今天:
2021-03-25 MySQL 8 通用表表达式 with as
2017-03-25 css 蒙层
2017-03-25 css 多行文本的溢出显示省略号(移动端)
2017-03-25 移动端利用-webkit-box水平垂直居中(旧弹性盒)
2017-03-25 去除inline-block元素间间距
2017-03-25 js定时器setInterval()与setTimeout()
点击右上角即可分享
微信分享提示