java协程框架quasar和kotlin中的协程对比分析


Posted in Java/Android onFebruary 24, 2022

前言

早就听说Go语言开发的服务不用任何架构优化,就可以轻松实现百万级别的qps。这得益于Go语言级别的协程的处理效率。协程不同于线程,线程是操作系统级别的资源,创建线程,调度线程,销毁线程都是重量级别的操作。而且线程的资源有限,在java中大量的不加限制的创建线程非常容易将系统搞垮。接下来要分享的这个开源项目,正是解决了在java中只能使用多线程模型开发高并发应用的窘境,使得java也能像Go语言那样使用协程的语义开发了。

快速体验

添加依赖

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

注意:目前quasar最高的版本是0.8.0,但是最高版本的只支持jdk11以上

添加java agent

quasar的实现原理是在java加载class前,通过jdk的instrument机制使用asm来修改目标class的字节码来实现的,他标记了协程代码的起始和结束的位置,以及方法需要暂停的位置,每个协程任务统一由FiberScheduler去调度,内部维护了一个或多个ForkJoinPool实例。所以,在运行应用前,需要配置好quasar-core的java agent地址,在vm参数上加上如下脚本即可:

-javaagent:D:\.m2\repository\co\paralleluniverse\quasar-core\0.7.10\quasar-core-0.7.10.jar

线程VS协程

下面模拟调用某个远程的服务,假设远程服务处理耗时需要1S,这里使用执行阻塞1S来模拟,分别看多线程模型和协程模型调用这个服务10000次所需的耗时

协程代码

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("结束了: " + stopWatch.prettyPrint());
    }

耗时情况:

java协程框架quasar和kotlin中的协程对比分析

多线程代码

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("结束了: " + stopWatch.prettyPrint());
    }

耗时情况

java协程框架quasar和kotlin中的协程对比分析

协程完胜

可以看到上面的结果,在对比访问一个耗时1s的服务10000次时,协程只需要2秒多,而多线程模型需要4秒多,时效相差了一倍。而且上面多线程编程时,并没有指定线程池的大小,在实际开发中是绝不允许的。一般我们会设置一个固定大小的线程池,因为线程资源是宝贵,线程多了费内存还会带来线程切换的开销。上面的场景在设置200个固定大小线程池时。结果也是可预见的达到了50多秒。这个结果足以证明协程编程ko线程编程了。而且在qps越大时,线程处理的效率和协程的差距就约明显,缩小差距的唯一方式就是增加线程数,而这带来的影响就是内存消耗激增。而反观协程,基于固定的几个线程调度,可以轻松实现百万级的协程处理,而且内存稳稳的。

后记

最后,博主以为Quasar只是一个框架层面的东西,所以就又去看了下同样是jvm语言的kotlin的协程。他的语言更简洁,可以直接和java混合使用。跑上面这种实例只需要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("结束了: " + stopWatch.prettyPrint())
}

当博主看到这个结果的时候,有种震惊的赶脚,kotlin的同步模型牛逼呀,瞬时感觉到发现了java里的骚操作了,可以使用kotlin的协程来代替java中的多线程操作。因为他们两个混合开发毫无压力。如果行的通,那就太爽了。所以就有下面这个kotlin协程实现的代码:

@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
    }
}

现实是,当我使用协程替换掉我java多线程写的一个多线程汇聚多个http接口的结果的接口时,通过ab压测他们两个的性能并没有很大的变化,最后了解到主要原因是这个时候,在协程里发起一个http的请求时,涉及到操作系统层面的socket io操作,io操作是阻塞的,协程的并发也就变成了调度协程的几个线程的并发了。而且当我把同样的代码放到Quasar中的时候,Quasar直接抛io异常了,说明Quasar还并不能轻松支持这个场景。那为什么上面的测试结果差距这么大呢,是因为我错误的把协程实现里的阻塞等同于线程的阻塞。协程里的delay挂起函数,会立马释放线程到线程池,但是当真正的io阻塞的时候也就和真正的线程sleep一样了,并没有释放当前的线程。所以这些对比都没有太大的意义

以上就是java协程框架quasar和kotlin中的协程对比分析的详细内容,更多关于java框架quasar和kotlin协程对比的资料请关注三水点靠木其它相关文章!

Java/Android 相关文章推荐
解析Java异步之call future
Jun 14 Java/Android
Java Dubbo框架知识点梳理
Jun 26 Java/Android
分析ZooKeeper分布式锁的实现
Jun 30 Java/Android
Java日常练习题,每天进步一点点(38)
Jul 26 Java/Android
在Spring-Boot中如何使用@Value注解注入集合类
Aug 02 Java/Android
关于springboot配置druid数据源不生效问题(踩坑记)
Sep 25 Java/Android
springboot 多数据源配置不生效遇到的坑及解决
Nov 17 Java/Android
利用Sharding-Jdbc进行分库分表的操作代码
Jan 22 Java/Android
解析探秘fescar分布式事务实现原理
Feb 28 Java/Android
InterProcessMutex实现zookeeper分布式锁原理
Mar 21 Java/Android
Netty分布式客户端处理接入事件handle源码解析
Mar 25 Java/Android
Java实现添加条码或二维码到Word文档
Jun 01 Java/Android
springmvc直接不经过controller访问WEB-INF中的页面问题
Feb 24 #Java/Android
正则表达式拆分url实例代码
Feb 24 #Java/Android
mybatis源码解读之executor包语句处理功能
Feb 15 #Java/Android
java executor包参数处理功能 
Feb 15 #Java/Android
Java如何实现通过键盘输入一个数组
Feb 15 #Java/Android
Java实现给Word文件添加文字水印
Feb 15 #Java/Android
SSM项目使用拦截器实现登录验证功能
Jan 22 #Java/Android
You might like
ZF等常用php框架中存在的问题
2008/01/10 PHP
php adodb操作mysql数据库
2009/03/19 PHP
PHP开发规范手册之PHP代码规范详解
2011/01/13 PHP
PHP仿博客园 个人博客(1) 数据库与界面设计
2013/07/05 PHP
php上传文件,创建递归目录的实例代码
2013/10/18 PHP
php根据日期判断星座的函数分享
2014/02/13 PHP
PHP基于curl后台远程登录正方教务系统的方法
2016/10/14 PHP
微信接口生成带参数的二维码
2017/07/31 PHP
php 截取中英文混合字符串的方法
2018/05/31 PHP
Flash+XML滚动新闻代码 无图片 附源码下载
2007/11/22 Javascript
THREE.JS入门教程(1)THREE.JS使用前了解
2013/01/24 Javascript
jquery操作cookie插件分享
2014/01/14 Javascript
jquery实现点击消失的代码
2014/03/03 Javascript
JavaScript获取Url里的参数
2014/12/18 Javascript
JavaScript、tab切换完整版(自动切换、鼠标移入停止、移开运行)
2016/01/05 Javascript
jQuery文字轮播特效
2017/02/12 Javascript
使用jQuery和ajax代替iframe的方法(详解)
2017/04/12 jQuery
JavaScript实现前端分页控件
2017/04/19 Javascript
整理一些最近经常遇到的前端面试题
2017/04/25 Javascript
对vux点击事件的优化详解
2018/08/28 Javascript
[03:16]DOTA2完美大师赛主赛事首日集锦
2017/11/23 DOTA
[01:01:13]2018DOTA2亚洲邀请赛 4.5 淘汰赛 Mineski vs VG 第三场
2018/04/06 DOTA
Python读取一个目录下所有目录和文件的方法
2016/07/15 Python
简单实现python收发邮件功能
2018/01/05 Python
分析Python读取文件时的路径问题
2018/02/11 Python
pytorch中tensor张量数据类型的转化方式
2019/12/31 Python
解决json中ensure_ascii=False的问题
2020/04/03 Python
FC-Moto英国:欧洲最大的摩托车服装和头盔商店之一
2019/08/25 全球购物
医药营销个人求职信
2014/04/12 职场文书
诚信考试倡议书
2014/04/15 职场文书
艾滋病宣传标语
2014/06/25 职场文书
演讲稿开场白台词
2014/08/25 职场文书
帝企鹅日记观后感
2015/06/10 职场文书
企业管理制度设计时要注意的几种“常见病”!
2019/04/19 职场文书
创业计划书之物流运送
2019/09/17 职场文书
Nginx反向代理学习实例教程
2021/10/24 Servers