Skip to content

Kotlinの协程

更新: 6/17/2025 字数: 0 字 时长: 0 分钟

众所周知,在Kotlin协程发布的时候,Java的 VirtualThread还不知道在哪呢,那么和Java互通的Kotlin是如何实现的协程呢?

基于Thread进行包装

这是目前主流的协程设计方案虽然实践不同,但Go语言实际上也是通过维护线程的方式实现的协程

而作为Google私生子的Kotlin在一定程度上借鉴了Go语言协程的优秀设计,同时有对老牌的Java多线程方案进行了参考谢谢你RxJava,也就形成了我们现在使用的Kotlin协程

协程的使用

kotlin
fun main(){  
  
    CoroutineScope(Dispatchers.IO).launch {  
        sleep(2000L)  
        println("协程内")  
    }  
  
    println("协程外")  
}

我们往往会使用CoroutineScope.launch{}的方式启动一个协程,协程的内容就是在代码块中执行的,其中协程和当前线程是并行的,也就是两者互不干预

CoroutineScope会要求我们传入一个参数,这个参数用于定义协程的上下文

这里的Dispatchers叫做协程调度器,用于定义协程是以何种方式运行(或者说是协程底层的线程池是如何实现的),常用的基本只有三个

  • Dispatchers.Default:用于CPU密集型操作
  • Dispatchers.IO:用于IO密集型操作
  • Dispatchers.Main:用于客户端程序,指明当前协程实在UI线程上运行,如果是服务端程序(比如SpringBoot程序),使用该调度器会报错

当然,Kotlin也为我们提供了自定义调度器的方法,但是不算常用,这里就不提了(希望在JDK25之后能够提供Dispatchers.VT版本的调度器)

父子协程

我们可以在协程的内部继续切换协程

kotlin
fun main(){  
  
    CoroutineScope(Dispatchers.IO).launch {  
        launch(Dispatchers.Default){  
            throw Exception("Error")  
        }  
        delay(3000L)  
        println("1")  
    }  
  
    sleep(10000L)  
}

其中内部的协程我们就称之为子协程,外部的协程我们就称之为父协程,子协程支持传入一个调度器用于切换当前协程所用的底部线程池实现(这样的设计方便了我们进行线程切换),当不传入调度器的时候则会使用与父协程一样的调度器(准确来说应该是使用父协程的上下文)

当子协程抛出异常时,父协程会受到牵连,一同中断

挂起

在Kotlin中我们会使用suspend关键字来将一个方法声明为挂起方法,这个关键字在编译上并没有什么实际的作用,只是提示开发者不要随意使用挂起方法

那么如何理解挂起呢?

我们知道协程采用的是一种协作式的多任务执行方式,允许当前方法在等待IO或其他操作的时候主动的让出CPU,等到自己等待的东西完成后再恢复到之前的状态。这个过程就被称之为挂起。

也就是说,当当前方法挂起之后,当前方法是未运行完毕的,是要等待自己等待的东西运行完成后再继续运行的。

在Kotlin中,我们可以使用withContext来将让当前的方法挂起,然后执行withContext中的任务,等待这个任务执行完毕后在继续执行

kotlin
suspend fun main(){  
    withContext(Dispatchers.IO){  
        delay(3000)  
        println("内部")  
    }  
    println("外部")  
}

需要注意的是,withContext必须要传入一个调度器,这个调度器用于指定内部任务要采取何种策略执行

运行结果

image.png

我们很明显的发现main方法主动等待withContext内部的任务执行完后才继续执行

挂起与协程

只有协程才会有挂起的说法,但需要注意的是,挂起与协程并非语言层面的东西,也就是所任何语言都可以实现协程与挂起

说回Kotlin,我们会使用挂起方法来将外部的协程挂起,然后去执行挂起方法内部的东西,等执行完毕后挂起的协程再恢复执行。

但是需要注意的是,被挂起的协程让出的资源并非一定是(可以是,可以不是)给到执行挂起方法的协程使用,二者只是两个并列的释放和使用的关系,然后在时间上存在一定的顺序性。

同样的,如果你只是单纯的使用协程,而不涉及挂起操作,那么在时间顺序上是和线程是没有区别的

本站访客数 人次      本站总访问量