协程,可以将它简单地理解成一种轻量级的线程。要知道,之前所学习的线程是非常重量级的,需要依靠操作系统的调度才能实现不同线程之间的切换。而使用协程却可以仅在编程语言的层面就能实现不同协程之间的切换,从而大大提升了并发编程的运行效率。
协程允许我们在单线程模式下模拟多线程编程的效果,代码执行时的挂起与恢复完全是由编程语言来控制的,和操作系统无关。这种特性使得高并发程序的运行效率得到了极大地提升。
为什么要使用协程?
- 轻量、高效
- 简单、好用
- 可以用同步的方式编写异步代码
协程的基本用法
添加依赖
首先要添加依赖库:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.1"
// 此依赖库是 Android 项目才会用到的,纯 Kotlin 程序其实用不到。
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1"
GlobalScope.launch 函数
最简单的开启一个协程的方式:使用 GlobalScope.launch 函数,它可以创建一个协程的作用域。并且创建的永远是顶层协程,这一点和线程比较像,因为线程也没有层级这一说,永远是顶层的。
fun main(){
// GlobalScope.launch 函数每次创建的都是一个顶层协程,这种协程当应用程序运行结束时也会跟着一起结束,所以,
// 代码块中的代码还没来得及运行,应用程序就结束了。解决的办法是:让程序延迟一段时间再结束。
GlobalScope.launch {
println("codes run in coroutine scope")
// delay 函数可以让当前协程延迟指定时间后再运行。
// delay 函数是一个非阻塞式的挂起函数,它只会挂起当前协程,并不会影响其它协程的运行。
// delay 函数只能在协程的作用域或其他挂起函数中调用。
// ----------------------------------------------------------------------
// 而 Thread.sleep() 会阻塞当前的线程,这样运行在该线程下的所有协程都会被阻塞。
delay(1500)
println("codes run in coroutine scope finished") // 不会运行
}
// 如果代码块中的代码不能在 1 秒钟内运行结束,那么就会被强制中断。
Thread.sleep(1000)
}
runBlocking 函数
借助 runBlocking 函数可以让应用程序在协程中所有代码都运行完了之后再结束。
注意:此函数通常只应该在测试环境下使用,在正式环境中使用容易产生一些性能上的问题。
fun main(){
// runBlocking 同样会创建一个协程的作用域,
// 但是它可以保证协程作用域的内的所有协程和子协程没有全部执行完之前一直阻塞当前线程。
runBlocking {
println("codes run in coroutine scope")
delay(1500)
println("codes run in coroutine scope finished")
}
}
launch 函数
使用 launch 函数可以创建多个协程,它必须在协程的作用域中才能调用,其次,它会在当前协程的作用域下创建子协程。子协程的特点是如果外层作用域的协程结束了,该作用域下的所有子协程也会一同结束。
fun main(){
runBlocking {
launch {
println("launch1")
delay(1000)
println("launch1 finished")
}
launch {
println("launch2")
delay(1000)
println("launch2 finished")
}
}
// 运行结果:
// launch1
// launch2
// launch1 finished
// launch2 finished
// 测试性能
test()
}
fun test(){
val start = System.currentTimeMillis()
runBlocking {
// 循环创建了 10 万个协程
repeat(100000){
launch {
println(".")
}
}
}
// 查看消耗时间
val end = System.currentTimeMillis()
println