2.协程-理解非阻塞挂起

本文介绍了Kotlin协程的基本使用,包括如何启动协程以及协程的关键特性——非阻塞式挂起。通过对比Java线程的实现,阐述了协程在执行耗时操作时如何不阻塞主线程,保持UI流畅。示例代码展示了在Android环境中,协程如何在主线程中进行非阻塞操作,避免ANR异常。
摘要由CSDN通过智能技术生成

上一篇,我们了解到了协程的作用,以及协程到底在干啥,有了对上一篇的了解,我们继续学协程就稍微的轻松了(只能说稍微轻松)。

如何使用协程

如何使用协程呢?

  • 1.协程依赖
    Gralde依赖版本查看

注意:Android 和 Java 依赖的库不一样哦,请自行查看。

  • 2.启动一个协程
fun main() {
 GlobalScope.launch { // 在后台启动一个新的协程并继续
     delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
     println("World!  ${Thread.currentThread()}") // 在延迟后打印输出
 }
 println("Hello,  ${Thread.currentThread()}") // 协程已在等待时主线程还在继续
 Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
}

上面的代码是不是一头雾水,这是官方给我们提供实例的代码,是不是看不懂呀?第一次看我也看不懂,那么接下来忘掉它,接下来看我提供的实例代码。
在这里插入图片描述

非阻塞挂起

接下来,我们来看下 2 断代码,一个是用 Kotlin 的协程来实现,一个是用 Java 的线程来实现。

fun main() {
    // 1.启动一个协程
    GlobalScope.launch {
        // 输出当前线程昵称
        println("1:${Thread.currentThread().name}")
        // 线程等待 1 秒
        delay(1000)
        // 输出当前线程昵称
        println("2:${Thread.currentThread().name}")
    }
    // 输出当前线程昵称
    println("3:${Thread.currentThread().name}")
    // 阻塞主线程,不让其停止
    Thread.currentThread().join()
}
输出结果:

3:main
1:DefaultDispatcher-worker-1
2:DefaultDispatcher-worker-1
public class TestJava {
    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("1:" + Thread.currentThread().getName());
                Thread.sleep(1000);
                System.out.println("2:" + Thread.currentThread().getName());

            }
        }).start();
        System.out.println("3:" + Thread.currentThread().getName());
        Thread.currentThread().join();
    }
}
输出结果:  

3:main
1:Thread-0
2:Thread-0

我们看下输出结果是不是感觉 2 断代码,实现的功能是一样的,都是开启另外一个线程执行一次等待 1 秒的操作,然后再次输出线程昵称。没错结果是一样的,但是真的就一样吗?
接下来我们就引入协程的一个关键 非阻塞式挂起 的概念。
在这里插入图片描述
要想理解 非阻塞式挂起 我们先解读上面的 Java 代码,我们首先通过 Thread 启动了一个线程,此时我们在线程中调用 Thread.sleep(1000) 时候,此时当前的线程就处于阻塞状态了。这个线程不能干其他事情了,它一直在等待 1 秒后的才会去执行后续的代码。
但是在实际开发中我们并不想这样,例如 Andorid 的 UI 线程,当我们希望它执行一个耗时操作的时候,也不能阻塞 UI 线程(因为界面渲染也依靠这个线程:ANR 异常),因此我们的耗时操作只能放在另外一个线程中,当耗时操作的线程操作完毕,再通知 UI 线程(这就是地狱回调)。
在这里插入图片描述
接下来,我们再看 协程 的关键代码。

// 1.启动一个协程
GlobalScope.launch {
    // 输出当前线程
    println("1:${Thread.currentThread().name}")
    // 线程等待 1 秒
    delay(1000)
    // 输出当前线程昵称
    println("2:${Thread.currentThread().name}")
}

在这里插入图片描述
我启动了一个协程,我使用的 delay(1000) 等待 1 秒,delay 是协程提供的一个 挂起函数,执行挂起函数的意义就是 不阻塞 当前线程,你可以去干其他事,我做完事情了在通知你继续帮我干事。
在这里插入图片描述
还是不理解?我们拿 Android 代码做一个实例。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        fab.setOnClickListener { view ->
            // 启动一个协程
            GlobalScope.launch(Dispatchers.Main) {
                Log.d("wyz", "1:${Thread.currentThread().name}  ${System.currentTimeMillis()}")
                delay(2000)
                Toast.makeText(this@MainActivity,"好舒服啊!",Toast.LENGTH_SHORT).show()
                Log.d("wyz", "2:${Thread.currentThread().name}  ${System.currentTimeMillis()}")

            }
        }
        ...
    }
}
输出结果:  
2019-12-24 10:57:59.767 8917-8917/com.protect.love D/wyz: 1:main  1577156279767
2019-12-24 10:58:01.782 8917-8917/com.protect.love D/wyz: 2:main  1577156281782

在这里插入图片描述
注意到我的代码是在主线程调用的 delay(2000) ,2秒后在主线程弹出了 Toast。大家注意到我主线程一直没有处于阻塞状态(因为我的 UI 没有卡顿)。
这就是协程的 非阻塞式挂起 ,我允许你执行耗时操作,但是我执行耗时我去干其他事情。大家注意到协程的 非阻塞式挂起 必须要执行的是一个挂起函数,这个挂起函数的作用就是执行耗时任务,并通知调用线程耗时执行完毕。
在这里插入图片描述

到这里,我想刚开始接触协程的大家应该和我一开始一样有点懵逼,为啥会懵逼呢?大伙想想这种 非阻塞式挂起 若只使用一个线程,在 Java 丶 Dalvik 丶 ART 虚拟机有可能实现吗?
其实是不可能实现的。那为啥我看似协程代码只写了一个线程呢?其实原因就在 delay(2000) 这个挂起函数,这个挂起函数会开启另外一个线程执行等待 2 秒函数,2 秒后再通知调用的线程,执行后续的代码。

到这里,我想大家就应该理解了协程的 非阻塞挂起 的概念了。也应该能推敲出挂起函数要做的事情就是:开启线程 -> 执行耗时操作 -> 通知调用线程继续。

补充

这时候在回看我上面的代码,看似他们实现的功能效果是一样的。但是 Java 只启动了一个线程,而协程为了达到非阻塞式挂起,在调用 delay 函数的时候启动了另外一个线程。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黄毛火烧雪下

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值