Kotlin-轻量级协程与线程执行比对分析
轻量级协程与线程执行比对分析
作用域构建器
除了由不同的构建器提供协程作用域之外,还可以使用 coroutineScope 构建器声明自己的作用域。它会创建一 个协程作用域并且在所有已启动子协程执行完毕之前不会结束。
runBlocking 与 coroutineScope 可能看起来很类似,因为它们都会等待其协程体以及所有子协程结束。主要 区别在于,runBlocking 方法会阻塞当前线程来等待,而 coroutineScope 只是挂起,会释放底层线程用于其他 用途。由于存在这点差异,runBlocking 是常规函数,而 coroutineScope 是挂起函数。
fun main() = runBlocking {
launch {
delay(1000)
println("my job1")
}
println("person")
coroutineScope {
launch {
delay(3000)
println("my job2")
}
delay(2000)
println("hello world")
}//如果其中的协程挂起,那么coroutineScope函数也会挂起
println("welcome")
}
RUN> 🏄🏄🏄🏄🏄🏄
person my job1 hello world my job2 welcome Process finished with exit code 0
image-20210611164701768
image-20210611164857193
Creates a CoroutineScope and calls the specified suspend block with this scope. The provided scope inherits its coroutineContext from the outer scope, but overrides the context's Job.
创建一个coroutinscope并使用此范围调用指定的挂起块。提供的作用域从外部作用域继承其coroutineContext,但覆盖了上下文的Job。
轻量级协程与线程执行比对分析:
对于协程的效果跟线程其实差不多,但是它们俩其实还是存在本质的区别的,总的来说协程是轻量级的,而线程是重量级的。所以下面来做下实验来看一下它们两者的区别:
先来看下协程的效果,这里创建N个协程然后输出个字母,当然我们可以用for循环来创建多个,这里采用Kotlin的方式来实现一下:
fun main() = runBlocking {
repeat(5000) {
launch {
delay(1000)
println("A")
}
}
println("Hello World")
}
repeat函数
@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
contract { callsInPlace(action) }
for (index in 0 until times) {//函数内部执行times循环
action(index)
}
}
Executes the given function action specified number of times.
执行给定action的函数times次。一个当前迭代基于0的索引会当成参数传给action
image-20210611165441396
比较容易,接下来咱们换成线程来实现同样的效果
fun main() {
repeat(5000) {
thread {
Thread.sleep(1000)
println("A")
}
}
println("Hello World")
}
RUN> 🏄🏄🏄🏄🏄🏄
[0.780s][warning][os,thread] Failed to start thread - pthread_create failed (EAGAIN) for attributes: stacksize: 1024k, guardsize: 4k, detached. Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached at java.base/java.lang.Thread.start0(Native Method) at java.base/java.lang.Thread.start(Thread.java:801) at kotlin.concurrent.ThreadsKt.thread(Thread.kt:42) at kotlin.concurrent.ThreadsKt.thread$default(Thread.kt:25) at com.shengsiyuan.coroutines.HelloKotlin9Kt.main(HelloKotlin9.kt:7) at com.shengsiyuan.coroutines.HelloKotlin9Kt.main(HelloKotlin9.kt) [0.944s][warning][os,thread] Failed to start thread - pthread_create failed (EAGAIN) for attributes: stacksize: 1024k, guardsize: 4k, detached. Error occurred during initialization of VM java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached Process finished with exit code 1
区别就出来啦!!!由于协程是用户态,没有个数的上限,而线程是内核态的,它需要依赖于系统,是有上限的,另外由于线程有切换的开销,所以在有些情况会能看到线程的执行速度要比协程要慢,当然在我电脑上木有看出来,验证出来的结论就是协程是轻量级的,而线程是重要级的。
repeat()函数的一个细节,这个细节可能比较难理解,但是理解了之后能够让我们更加正确的用Kotlin的思维去编写代码,先来看一下它的参数定义:


我们看到的repeat()的第二个参数的Lambda表达式是要求要能接收一个参数呢,那怎么我们就能传lauch()函数给repeat()的第二个参数呢?

这也是Kotlin跟Java不一样的地方,也就是如果只有一个参数时在写Lambda表达式是可以省略的,接下来我们来把这个隐含的参数打出来:

这个细节非常之小,可能不一定每个人都能去研究它,但是研究透了之后其实也就是让我们更好的能使用Kotlin的一个基石。