例子一:

GlobalScope.launch(Dispatchers.Main) {    //开启子协程    withContext(Dispatchers.IO) {        for (i in 0 until 1000) {        }        Log.d("MainActivityXX", "withContext-> thread:" + Thread.currentThread().name)    }    Log.d("MainActivityXX", "GlobalScope-> thread:" + Thread.currentThread().name)}Log.d("MainActivityXX", "onCreate-> thread:" + Thread.currentThread().name)

打印log:

2022-10-09 20:24:21.100 8371-8371/com.example.xiecheng D/MainActivityXX: onCreate-> thread:main2022-10-09 20:24:21.131 8371-8412/com.example.xiecheng D/MainActivityXX: withContext-> thread:DefaultDispatcher-worker-12022-10-09 20:24:21.258 8371-8371/com.example.xiecheng D/MainActivityXX: GlobalScope-> thread:main

例子二:

GlobalScope.launch(Dispatchers.Main) {    //开启子协程    withContext(Dispatchers.IO) {        for (i in 0 until 1000) {        }        Log.d("MainActivityXX", "withContext1-> thread:" + Thread.currentThread().name)        withContext(Dispatchers.IO) {            for (i in 0 until 1000) {            }            Log.d("MainActivityXX", "withContext2-> thread:" + Thread.currentThread().name)        }    }    withContext(Dispatchers.IO) {        for (i in 0 until 1000) {        }        Log.d("MainActivityXX", "withContext3-> thread:" + Thread.currentThread().name)    }    Log.d("MainActivityXX", "GlobalScope-> thread:" + Thread.currentThread().name)}Log.d("MainActivityXX", "onCreate-> thread:" + Thread.currentThread().name)

打印log:

onCreate-> thread:mainwithContext1-> thread:DefaultDispatcher-worker-1withContext2-> thread:DefaultDispatcher-worker-1withContext3-> thread:DefaultDispatcher-worker-1GlobalScope-> thread:main

主线程开启一个协程,并不会阻碍主线程的执行,单个协程内部是串行执行的,这里整一个都是串行执行的是因为withContext是一个挂起函数。

GlobalScope.launch(Dispatchers.Main) {    //开启子协程    GlobalScope.launch (Dispatchers.IO) {        Log.d("MainActivityXX", "withContext-> thread:" + Thread.currentThread().name)    }    Log.d("MainActivityXX", "GlobalScope-> thread:" + Thread.currentThread().name)}

打印log:

GlobalScope-> thread:mainwithContext-> thread:DefaultDispatcher-worker-1

例子二:

GlobalScope.launch(Dispatchers.Main) {    //开启子协程    withContext(Dispatchers.IO) {        get()        Log.d("MainActivityXX", "withContext-> thread:" + Thread.currentThread().name)    }    Log.d("MainActivityXX", "GlobalScope-> thread:" + Thread.currentThread().name)}
suspend fun get() {
Thread {
Thread.sleep(5000)
Log.d("MainActivityXX", "get-> thread:" + Thread.currentThread().name)
}.start()
}

打印log:

2022-11-01 20:56:25.220 20058-20100/com.example.xiecheng D/MainActivityXX: withContext-> thread:DefaultDispatcher-worker-12022-11-01 20:56:25.264 20058-20058/com.example.xiecheng D/MainActivityXX: GlobalScope-> thread:main2022-11-01 20:56:30.222 20058-20103/com.example.xiecheng D/MainActivityXX: get-> thread:Thread-8

协程里面开启线程,那么线程不会受协程影响。

例子三:

GlobalScope.launch(Dispatchers.Main) {    fetchDocs()}suspend fun fetchDocs() {                             // Dispatchers.Main    val result = get("https://developer.android.com") // Dispatchers.IO for `get`    Log.d("MainActivityXX", "fetchDocs-> thread:" + Thread.currentThread().name) }//withContext本身就是挂起函数suspend fun get(url: String) = withContext (Dispatchers.IO) {    Log.d("MainActivityXX", "get-> thread:" + Thread.currentThread().name)}

打印log:

get-> thread:DefaultDispatcher-worker-1fetchDocs-> thread:main

使用了挂起函数,会先等待get方法执行完再执行下面得操作。

1、挂起函数并不会阻塞其所在线程,这样就极大地提高了线程的并发灵活度,最大化了线程的利用效率。
当在 ThreadA 上运行的 CoroutineA 调用了delay(1000L)函数指定延迟一秒后再运行,ThreadA 会转而去执行 CoroutineB,等到一秒后再来继续执行 CoroutineA

2、withContext本身就是挂起函数。

3、挂起的对象是协程。

4、这个 suspend 关键字,既然它并不是真正实现挂起,它其实是一个提醒。函数的创建者对函数的使用者的提醒:我是一个耗时函数,我被我的创建者用挂起的方式放在后台运行,所以请在协程里调用我。
为什么 suspend 关键字并没有实际去操作挂起,但 Kotlin 却把它提供出来?
因为它本来就不是用来操作挂起的。挂起的操作 —— 也就是切线程,依赖的是挂起函数里面的实际代码,而不是这个关键字。
所以这个关键字,只是一个提醒。
所以,创建一个 suspend 函数,为了让它包含真正挂起的逻辑,要在它内部直接或间接调用 Kotlin 自带的 suspend 函数,你的这个 suspend 才是有意义的。

5、所以这个 suspend,其实并不是起到把任何把协程挂起,或者说切换线程的作用。真正挂起协程这件事,是 Kotlin 的协程框架帮我们做的。
所以我们想要自己写一个挂起函数,仅仅只加上 suspend 关键字是不行的,还需要函数内部直接或间接地调用到 Kotlin 协程框架自带的 suspend 函数才行。

6、开发者需要明白,协程是运行于线程上的,一个线程可以运行多个(可以是几千上万个)协程。线程的调度行为是由 OS 来操纵的,而协程的调度行为是可以由开发者来指定并由编译器来实现的。当协程 A 调用 delay(1000L) 函数来指定延迟1秒后再运行时,协程 A 所在的线程只是会转而去执行协程 B,等到1秒后再把协程 A 加入到可调度队列里。所以说,线程并不会因为协程的延时而阻塞,这样可以极大地提高线程的并发灵活度。