Kotlin 协程真的比 Java 线程更高效吗?

  • 时间:
  • 浏览:0

到了这里,是不是 说Kotlin 完完会 不支持协程的呢?我认为这名 说法也是不准确的,只有说Kotlin-JVM 这名 组合是不支持协程的。这名 我门歌词 儿在IDEA中新建Kotlin工程的已经 。

我门歌词 儿知道在主流JVM的实现中,是没哟协程的,实际上JVM也他不知道上层的JVM语言到底是啥,反正JVM只认class文件,至于这名 class文件是Java编译出来的,还是Kotlin编译出来的,或是如groovy等某些语言,那完会 重要,JVM不只有知道。

经过前文的分析,我门歌词 儿知道为宜目前来看主流的JVM实现中是没哟协程的实现的。但会 可能有不少团队在朝着这方面努力,比如说 quasar这名 库,利用字节码注入的妙招还可以实现协程的效果。

1.Kotlin-JVM中所谓的协程是假协程,本质上还是一套基于原生Java Thread API 的封装。和Go中的协程完整篇 完会 兩个 东西,太满混淆,更谈不上哪此性能更好。

https://kotlinlang.org/docs/reference/native/concurrency.html(Kotlin-native平台而是直接将Kotlin-native编译成对应平台的可执行文件也而是机器码,太满只有这名 于JVM而是 的虚拟机了)。

N个并发任务分别打印不同的字符串。就跟上述Go和Java的例子一样。

start0 妙招最终调用的JVM_StartThread妙招. 再看看这名 妙招。

最近几年协程的概念越来很深入人心,主要还是可能Google的Go语言应用范围没哟广,考虑到目前并没兩个 通用的协程的定义,好多好多 本文中对协程的定义主要来自于Go。

比较好的实现妙招是用RxJava的zip操作符来做,在有了Kotlin已经 ,可能利用Kotlin,这段代码甚至会比zip操作符只有简单。这名 :

但会 会带来兩个 哪此的问题,当系统tcp连接数量超过CPU的核心数量的已经 缘何办?当然是有的系统tcp连接先暂停一下,但会 让某些的系统tcp连接走走,每个系统tcp连接完会 可能走一下,最终的目标而是让每个系统tcp连接都执行完毕。

Start兩个 系统tcp连接已经 ,这里最终是要调用兩个 jni妙招

OpenJDK正在做JVM的协程实现,项目名称为loom,有兴趣的同学还可以查看对应资料。

此人 理解Kotlin-JVM的系统tcp连接应该就仅仅是针对Java中的Thread做了一次更友好的封装。我要们更方便的使用Java中的系统tcp连接才是Kotlin-JVM中的协程的真正目的。

/src/share/vm/prims/   下面的 jvm.cpp 文件

我门歌词 儿都知道任何一门现代语言都对外提供了一定的并发能力,且一般完会 语言层面提供了“锁”的实现。比如开启10个系统tcp连接 对兩个 int变量 进行++操作,要保证打印出来的顺序一定得是1,2,3,4...10. 而是 的Java代码很好写,兩个 synchronized关键字就还可以,我门歌词 儿看看Go中的协程是不是 有这名 的能力?

这名 例子说明用Java开10w个系统tcp连接很为宜率就会OOM了,但会 Kotlin开10w个协程就太满再OOM,给人这名 Go语言中协程的感觉。但会 真的是而是 么?带着这名 哪此的问题,我门歌词 儿进行了一番探索,希望下面的内容能帮你解开疑惑。

每一次Thread的 上下文切换完会 带来开销,最终结果而是可能系统tcp连接太满,没哟最终系统tcp连接执行代码的时间就变少,可能大主次的CPU的时间都消耗在了切换系统tcp连接上下文上。

哪此的问题的讨论起源于文章《Go语言老要老出后,Java还是最佳选者 吗?》,可能已经 写过一段时间Go语言,对Go语言有一定的理解,好多好多 当时我都看这篇文章的已经 感到疑惑的是Kotlin到底有没哟完整篇 的实现这名 于Go语言中的协程机制?可能有,没哟显然没哟必要费没哟一大段功夫来魔改JVM的实现。可能没哟,没哟网上那一堆堆的博客难道说的完会 错误的吗?这名 下面百度搜索的结果:

最终:

https://www.zhihu.com/question/263955521

还可以从下图中看出来,这名 交错的并发任务在Go中是还可以在兩个 系统tcp连接中完成的,也就验证了协程的并发能力并完会 系统tcp连接给的,而是交给Go语言这名 此人 来完成的。

基于这名 讨论 我门歌词 儿还可以选者 的是,Kotlin语言没哟提供锁的关键字,所有的锁实现都交给了JVM此人 除理。就有而是而是交给系统tcp连接来除理了。也却语录,就有而是 Kotlin-JVM 声称此人 是协程,但实际上干活的还是JVM中Thread那一套东西。

同去在Go中创建兩个 协程,也仅仅只有4kb的内存而已,这跟OS中创建兩个 系统tcp连接所只有的1mb相差甚远。

 Kotlin官方网站中的例子:

2.Kotlin-JVM中所谓的协程挂起,而是开启了兩个 子系统tcp连接去执行任务(太满再阻塞而是 Thread的执行,要理解对于CPU来说,在宏观上每个系统tcp连接得到执行的概率完会 相等的),仅此而已,没哟哪此某些高深的东西。

简单来说有几只个并发任务,最终反应到JVM和OS中而是有几只个Thread来运行。但会 我门歌词 儿来看看Go语言中协程是如可完成这名 的事情的。

在hotspot 实现下(注意完会 jdk目录了):

关于这名 讨论,JB Team的意思是说 Kotlin 在此人 的语言级别并没哟实现这名 同步机制,还是依靠的 Kotlin-JVM中的 Java关键字。尤其是synchronized。既然并发的机制完会 依靠的JVM中的sync可能是lock来保证,缘何称之为此人 是协程的?

在Go语言中,哪此并发的任务之间相互的调度完会 由Go语言完成,由极少数的系统tcp连接来完成n个协程的并发任务,这其中的调度器并没哟交给操作系统而是交给了此人 。

这里要提一下的是,好多好多 人都以为Kotlin是谷歌出的,是谷歌的亲儿子,实际上这是这名 错误的想法。Kotlin是JB Team的产物,并完会 谷歌亲自操刀开发的,最多是不是 个谷歌的干儿子。这名 JB Team 好多好多 人应该知道,是IDEA的开发团队Android Studio也是脱胎自 IDEA。  

写兩个 简单的代码验证一下,简单写兩个 Kotlin的类,可能Kotlin这名 没哟提供同步的关键字,好多好多 这里就用Kotlin官方提供的sync注解。

5.对于Java来说,不管你用哪此妙招,但会 你没哟魔改JVM,没哟最终你代码里start几只系统tcp连接,操作系统就会创建几只系统tcp连接,是1比1的关系。

网上几乎完整篇 介绍Kotlin的文章完会 说Kotlin的协程是多么的高效,比系统tcp连接性能好好多好多 ,然而事情的真相简直没哟么?

但会 我门歌词 儿反编译看看这名 东西到底是啥。

执行结果很清楚的还可以都看,Go中的协程也是有完整篇 的锁实现的。没哟Kotlin-JVM的协程有没哟这名 的锁的实现呢?经过一番搜索,我门歌词 儿首先看看这名 Kotlin官方论坛中的讨论https://discuss.kotlinlang.org/t/concurrency-in-kotlin/858

我为宜翻译一下其中的几只要点:Kotlin-Native的并发能力不鼓励使用中含互斥代码块和条件变量的经典的面向系统tcp连接的并发模型,可能该模型容易出错且不可靠。开篇的这句话直接diss的而是JVM的并发模型。但会 继续往下看还有惊喜:

Kotlin官网中那个创建10w个Kotlin协程没哟oom的例子就有而是有误导性,本质上那10w个Kotlin协程而是10w个并发任务仅此而已,他下面运行的而是兩个 单系统tcp连接的系统tcp连接池。你往兩个 系统tcp连接池后边丢几只个任务完会 会OOM的(前提你没哟的系统tcp连接池创建的已经 设定了对应的拒绝策略,但会 无界队列下,任务太满完会 OOM),可能在运行的始终是那几只系统tcp连接。

再比如某个Kotlin的视频教程(我仔细观都看其中关于协程主次的讲解,与网络上流传的诸如协程比系统tcp连接高效是基本一致的)

也却语录但会 在Java语言后边每start Thread 一次,JVM中就会多兩个 Thread,最终就会多兩个 os级别的系统tcp连接,在不考虑调整JVM参数的情况汇报下,兩个 Thread所占用的内存大小是1mb。最终的JVM的Thread的调度还是依赖底层的操作系统级别的Thread调度。只而是依赖了操作系统级别的Thread调度,没哟就不可除理的处在Thread切换带来的开销。

对于大主次Java的开发者来说,JVM完会 Oracle提供的,而Android开发者面对的而是Art了。但会 不管是Oracle的JVM还是谷歌Android的Art,对于这名 主流的JVM实现,我门歌词 儿的系统tcp连接数量和操作系统中系统tcp连接的数量基本完会 保持在1:1的。

没哟既然证明了,Kotlin-JVM中的协程并完会 真协程,没哟这名 东西到底是哪此,应该缘何用?

https://www.zhihu.com/question/232902400

找到这名 妙招:

协程的概念这名 太满新鲜,使用C++打上去内嵌汇编,兩个 基本的协程模型400行代码之内就还可以完整篇 搞出来。早在2013年国内完会 团队开源了号称支持千万并发的C++协程库 libco。

在这名 作者加入Oracle已经 ,OPENJDK也老要在往协程上努力,项目名loom,这名 应该是开源社区中老要在做的标准协程实现了。此外在生产环境中可能协程上线的效果还可以看文章《重塑云上的 Java 语言》。

jdk 目录下 /src/share/native/java/lang/ 目录下查询Thread.c 文件

还可以看出来,我门歌词 儿的兩个 请求分别在不一样的Thread中完成,但会 回调到主系统tcp连接的时机也差太满花了5s的时间,证明这名 个request是并行请求的。

考虑这名 稍微复杂化的场景,某个页面只有兩个 接口都返回已经 也能刷新展示,此种需求,可能用原生的Java concurrent并发包是还可以做的,但会 比较麻烦,要考虑各种异常带来的哪此的问题。

这段代码很简单,还可以多看一下注释。好多好多 人完会 被所谓Kotlin协程的非阻塞式吓到,就有而是你就理解成Kotlin中所宣传的非阻塞式,无非是用阻塞的写法来完成非阻塞的任务而已。

3.Kotlin-Native是有可能实现完整篇 真协程方案的。就有而是我此人 不认为JB TEAM 在这方面能比Go做的更好,好多好多 这名 项目意义并完会 很大。

我门歌词 儿只有注意的是:对于开发者而言,太满关心实现并发任务的到底是系统tcp连接还是系统tcp连接还是协程可能是哪此某些。我门歌词 儿只关心提交的并发任务是不是 还可以完成。

再回到协程,尤其是在Go语言老要老出已经 ,协程在很大程度上还可以除理可能创建系统tcp连接太满,最终原困CPU时间片都来做切系统tcp连接的操作,从而留给系统tcp连接此人 的CPU时间过少的哪此的问题。

注意看第语录,意思而是Kotlin-native提供了这名 worker的机制 来替代系统tcp连接。目前来看能替代系统tcp连接的东西也就只有协程了。也却语录起码在Kotlin-native这名 平台上,Kotlin是真的想提供协程能力的。目前Kotlin-Native并没哟正式发布,我门歌词 儿在idea上新建Kotlin工程的已经 并没哟都看有Kotlin-Native这名 选项。且Kotlin-Native目前仅支持linux和mac平台,不支持windows。有兴趣且有条件的同学还可以自行搜索Kotlin-Native的编译妙招。

在打印的已经 只有打印出所属的系统tcp连接id可能系统tcp连接name,且这id和name要保证一样。可能只有一样 才还可以证明是在兩个 系统tcp连接上完成了并发任务,而完会 靠JVM的Thread来完成并发任务。

这名 执行结简直的很简单, 交错打印的IQOO和x27 分别对应着兩个 独立的系统tcp连接。好多好多 Java 对外提供的并发能力而是依靠不同的Thread来完成。

继续下去就跟平台有关了,考虑到Android底层而是Linux,且现在基本服务器完会 部署在Linux环境下,还可以直接在Linux目录下找对应的实现:也即是在hotspot 下 src/os/linux/vm/os_linux.cpp 中找到该入口。

https://juejin.im/post/5b7678f451882533110e8948

但会 知道 Go中的多个协程还可以在同兩个 系统tcp连接上执行并发任务即可。还可以理解为Go的并发模型是M(协程数):N(系统tcp连接数)。其中M远远大于N(指数级的差距). 这名 是所有实现协程机制的语言中共有的形状。

试想一下,我门歌词 儿上述Kotlin中的代码 可能用Thread来写,就会比较麻烦了,甚至还只有用到回调(可能你太满再handler语录)。这名 点上Kotlin 协程的作用和RxJava就有而是是一致的,只不过Kotlin做的更彻底,比RxJava更优雅更方便更简洁。

最后对本文做兩个 总结:

4.Kotlin-JVM中的协程最大的价值是写起来比RxJava的系统tcp连接切换只有方便。几乎而是用阻塞的写法来完成非阻塞的任务。

那同样的需求,用Kotlin-JVM还可以来完成吗?答案是不还可以。简单来说,可能Kotlin-JVM 能提供Go这名 的协程能力,那应该能完成如下的需求(但实际上使用Kotlin语言是无法完成下面的需求的):

还可以看出来,这里是有选项的,上述的验证,我门歌词 儿只验证了 Kotlin-JVM 是不支持协程的。没哟有没哟这名 Kotlin-x 的东西是支持协程的呢?答案是还真可能有。具体参见官方文档中Kotlin-Native 平台对 并发能力的描述:

这里要额外注意的是,Go中 有随完会 老要老出协程迁移的情况汇报(即某个协程可能一日后现在开始 在系统tcp连接id为5的系统tcp连接跑,过一会又会去系统tcp连接id为10的系统tcp连接跑),这与Go的调度器机制有关,此处就不展开Go调度器这名 话题。

原困就在于Go语言中提供的协程在完成我门歌词 儿开发者只有的并发任务的已经 , 它的并发之间的调度是由Go语言这名 完成的,并没哟交给操作系统级别的Thread切换来完成。也而是协程本质上不过是兩个 个并发的任务而已。

要搞清楚协程,首没哟搞清楚系统tcp连接。我门歌词 儿都知道CPU的每个核心同一时刻只有执行兩个 系统tcp连接。

来看一下这段极简的Java代码。

熟悉Linux的人应该知道,pthread_create 函数而是Linux下创建系统tcp连接的系统函数了。这就完整篇 的证明了主流JVM中 Java代码里Thread和最终对应os中的Thread是1:1的关系。

本质上和Handler,AsyncTask,RxJava 基本是一致的。只不过Kotlin中的协程比我门歌词 儿更方便某些。这其中最核心的是suspend这名 Kotlin协程中的关键字。

这里简单证明一下,在Java中Thread和OS的Thread 是1:1的关系: