Kotlin中的异步编程:https://kotlinlang.org/docs/async-programming.html#threading
一些处理异步的方案:
协程Coroutines
kotlinx.coroutines 协程库,包含很多丰富的api
使用协程api需要添加依赖,java用java依赖,android用android依赖
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
}
协程是一个可暂停运行的实例,它需要在block代码块中来运行。协程并不绑定到任何特定的线程。
协程可以在一个线程中暂停,在另一个线程中唤醒。
协程可以被认为是轻量级线程,但有很多重要的区别
Your first coroutine
runBlocking launch 都是协程库里的函数
fun main() {
runBlocking {
launch {
delay(1000)
println("world")
}
launch {
delay(500)
println("world2222")
}
println("hello")
delay(3000)
}
// 先执行上面的协程代码阻塞并执行完毕,最后执行下面这一行
println("over")
}
先输出hello,0.5秒后输出world2222,1秒后输出world,最后输出over
launch{} 是一个协程构建者,它启动一个协程block代码块,该block独立运行。
delay() 是一个特殊的暂停or悬挂函数
runBlocking{} 也是一个协程构建者,它运行一个协程并中断阻塞当前线程,直到协程block中的代码运行完。
设计目的就是为了让普通代码具有暂停or悬挂or阻塞功能,可以放到主函数或者测试中使用。真实代码很少使用
Structured concurrency 结构化并发概念:阐述了协程的生命周期及运行范围。
协程的启动及运行只能在特定的协程范围CoroutineScope内。 协程范围外的代码必须要等协程内部block代码运行外才能运行。println(“over”)必须要等待runBlocking{}执行完,才能运行。
Extract function refactoring
可以将协程block中的代码,抽取成一个方法,用suspend修饰符
fun main() = runBlocking { // this: CoroutineScope
launch { doWorld() }
println("Hello")
}
// this is your first suspending function
suspend fun doWorld() {
delay(1000L)
println("World!")
}
Scope builder coroutineScope{}函数
协程代码块可以通过不同的构建器生成,也可以通过coroutineScope{}来生成。通过 coroutineScope{}来生成的协程,必须要等其及子项的block代码块运行完,才能结束。
runBlocking和coroutineScope的构建者可能看起来很相似,
主要区别在于:
runBlocking{}阻塞当前线程,其他资源不能被使用。
coroutineScope{}暂停当前线程,释放底层线程供其他使用,所以其是一个suspend暂停函数
可以在任何suspend暂停函数中使用coroutineScope{}
fun main() = runBlocking {
doWorld("World!", 2000)
println("over")
}
suspend fun doWorld(str: String, delayTime: Long) = coroutineScope {
launch {
delay(delayTime)
println(str)
}
println("hello")
}
coroutineScope{}可以在suspend暂停函数中执行并发操作
比如开启多个launch{} ,多个协程运行完,coroutineScope{}才结束
suspend fun doWorld() = coroutineScope { // this: CoroutineScope
launch {
delay(2000L)
println("World 2")
}
launch {
delay(1000L)
println("World 1")
}
println("Hello")
}
An explicit job: Job类
launch {} 函数的返回值是Job对象
job.join() 暂停协程,等待job及其子协程完成
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job{}
val job = launch { // launch a new coroutine and keep a reference to its Job
delay(1000L)
println("World!")
}
println("Hello")
job.join() // wait until child coroutine completes
println("Done")
Coroutines ARE light-weight 协程轻便,占用资源少
试试下面的代码
fun main() = runBlocking {
repeat(100_000) { // launch a lot of coroutines
launch {
delay(5000L)
print(".")
}
}
}
改用原始方式:
去掉runBlocking{} ,launch{}替换为Thread,delay()改为Thread.sleep, 然后再次运行试试
Thread(){
Thread.sleep(5000)
print(".")
}.start()
协程的取消
job.cancel() job.join()要配合使用
或者用job.cancelAndJoin()
判断协程状态用属性 isActive 当前协程还在处理中,没有完成也没有被取消
val job = launch {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancel() // cancels the job
job.join() // waits for job's completion
// cancel() join()要配合使用
println("main: Now I can quit.")
不可取消的协程
用withContext(NonCancellable) {}
val job = launch {
try {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
} finally {
withContext(NonCancellable) {
println("job: I'm running finally")
delay(1000L)
println("job: And I've just delayed for 1 sec because I'm non-cancellable")
}
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
协程的超时
用withTimeout(){}函数,超时会报异常
withTimeoutOrNull(){}函数,超时返回null
withTimeout(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}