Kotlin中的协程,用起来原来这么简单?

Kotlin中的协程,用起来原来这么简单?

协程和线程

线程:

在Java中,线程大家应该都很熟悉了,我就不做过多的解释了。java中的线程实现是和操作系统中的线程是一对一的关系,所以在java中线程的创建和销毁是很重量级的操作,当然使用线程池是一种很好的优化方案。

协程

众所周知,线程可以说是轻量级的进程,那么协程就可以说是轻量级的线程。在Java中线程受操作系统调度,jvm对线程没有太多控制权,而协程的控制权是掌握在程序本身的,是可以用代码控制的。不仅如此,协程的创建和销毁是非常轻量级的操作,有多轻量级呢?在Kotlin中,协程就是一个对象实例,这下明白了吧,创建协程就像创建普通对象一样简单。

协程的使用

准备工作

首先你得要有一定的Kotlin语言基础,不懂的地方可以去Kotlin中文官网学习

要在Kotlin中使用协程,首先在项目中导入Kotlin协程核心库,kotlinx-coroutines-core,Kotlin协程核心库是使用Kotlin协程必须导入的,当然还可以根据项目的不同再导入其他扩展库。这里仅需要导入协程核心库即可。

开始使用

启动一个协程

启动一个协程的方法可多了去了,Kotlin提供了很多启动协程的扩展方法,这里就讲讲常用的几种,直接上代码:

fun main() = runBlocking {
   
    println("runBlocking协程")
    GlobalScope.launch {
   
        println("GlobalScope.launch协程")
    }.join() // 这里的join现在不理解没关系
    GlobalScope.async {
   
        println("GlobalScope.async协程")
    }.await() // 这里的await现在不理解没关系
}

执行结果如下:

runBlocking协程
GlobalScope.launch协程
GlobalScope.async协程

我们重点看runBlocking,GlobalScope.launch,GlobalScope.async这三个方法。这三个方法中任一方法执行都会创建并启动一个协程。


首先看runBlocking的方法定义:

public fun <T> runBlocking(...): T

方法参数暂时不用管,所以这里省略了,runBlocking方法是一个泛型方法,并且是Kotlin中的顶层方法,在任何地方调用runBlocking方法后都会创建一个运行在当前线程的协程,直到runBlocking方法返回后才会运行后面的代码逻辑。由于runBlocking的这个特性,runBlocking的用途很有限。


再来看一下GlobalScope.launch方法定义:

public fun CoroutineScope.launch(...): Job

同样省略了方法参数,CoroutineScope.launch也是一个Kotlin中的顶层方法,啥,方法名错了?说好的GlobalScope.launch呢?并没有错,GlobalScope.launch调用的就是CoroutineScope的扩展方法launch,不信你看GlobalScope类定义:

public object GlobalScope : CoroutineScope

GlobalScope继承自CoroutineScope, 所以GlobalScope.launch调用的就是CoroutineScope.launch方法,从方法定义可以看到,它返回的是一个Job对象,这里先不用深究Job对象有什么用,你暂且只要知道它可以用来启动或者取消这个协程就行了。


接下来看GlobalScope.async方法定义:

public fun <T> CoroutineScope.async(...): Deferred<T>

按照惯例,省略方法参数,聪明的小伙伴一定猜到了,GlobalScope.async调用的就是CoroutineScope.async方法。没错,就是这么回事。CoroutineScope.async同样是Kotlin的顶层方法。也是一个泛型方法,从方法定义可知它返回一个Deferred对象,那这个Deferred对象是干嘛的呢,Deferred翻译过来就是延期的意思,这个对象有一个重要的方法就是await方法,这个方法被调用后,如果结果已经可用,那么会直接返回结果,如果结果不可用,那么会暂停调用方协程,直到await结果可用返回或者有异常从中抛出。


上面说了这么多次顶层方法,那么为什么要定义成顶层方法呢?因为在Kotlin中顶层方法是可以在任何地方都能调用的,当然前提是权限允许。这样就可以在任何地方创建协程了。以上就是在Kotlin中启动一个协程的方法,当然启动协程的方法b不只这些,这里只是介绍了一部分而已。

runBlocking,阻塞线程的协程

调用runBlocking方法后会创建并启动一个协程,并且只有待到runBlocking中的代码逻辑全部执行完成后,runBlocking后面的代码逻辑才会得到执行,来,看个例子:

fun main() {
   
    println("我在runBlocking之前 ${
     System.currentTimeMillis()}")
    runBlocking {
   
        println("我在runBlocking之中 ${
     System.currentTimeMillis()}")
        delay(2000) // 在协程中调用该方法会暂停当前协程指定时间,并不会阻塞线程
    }
    println("我在runBlocking之后 ${
     System.currentTimeMillis()}")
}

执行结果:

我在runBlocking之前 1588400909039
我在runBlocking之中 1588400909040
我在runBlocking之后 1588400911043

从结果不难看出runBlocking之后的代码是在runBlocking结束之后才得到执行的,所以可以得出结论: runBlocking会阻塞当前线程直到协程中的任务执行完成才会返回继续执行runBlocking后面的代码逻辑。那么这样的阻塞式协程在何时会用到呢?这个问题其实很简单,runBlocking其实可以和main方法无缝衔接使用,以下写法可以说是runBlocking的典型用法了:

fun main() = runBlocking {
   
    // 代码逻辑
}

像上面这种写法,在学习Kotlin协程时是很有用的,就是因为它会阻塞当前线程的这一特性。现在可能有人就会问: 既然有阻塞线程的协程,那应该有不阻塞线程的协程吧。答案是肯定的,其实绝大多数协程都是不会阻塞线程的,线程中的阻塞可以和协程中的暂停对应,但不能混作一谈,在协程中多个协程是依靠暂停来相互协作的。下面就来看看不阻塞线程的协程。

launch,不阻塞线程的协程

刚才我们讲了阻塞线程的协程,现在就讲讲不阻塞线程的协程。直接调用GlobalScope.launch就会创建并启动一个协程,并且该方法会直接返回。先看一段代码直观体验一下:

fun main() {
   
    println("main方法开始")
    GlobalScope.launch {
   
        println("GlobalScope.launch启动")
        delay(1000) // 防止协程过快结束
        println("GlobalScope.launch结束")
    }
    println("main方法结束")
}

执行结果:

main方法开始
main方法结束
GlobalScope.launch启动

从执行结果来看,可以发现虽然协程的创建是在println("main方法开始")println("main方法结束")之间,但是协程真正运行是在println("main方法结束")之后,同时由于main方法结束,协程也就随之结束,只打印出了“GlobalScope.launch启动”,而没有后续了。像这样的协程就是不阻塞线程的协程。

假如你现在有这么一个需求,你现在需要执行一段非常耗时的操作,而当前线程有无法等待这么久,就像Android中的UI线程那样,像这样的问题你会怎么处理呢?答案很简单,相信你也很快就想到解决办法了,把耗时操作丢到子线程中去处理就行了呀,对就是这么简单粗暴。但有了协程之后,我们就可以这些写:

fun main() = runBlocking {
   
    println("runBlocking开始")
    launch {
   
        var count 
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值