Kotlin協程入門

  • 2021 年 9 月 24 日
  • 筆記

開發環境

  • IntelliJ IDEA 2021.2.2 (Community Edition)
  • Kotlin: 212-1.5.10-release-IJ5284.40

介紹Kotlin中的協程。用一個例子來展示協程的基本用法。

第一個例子

新建工程

我們使用的是社區版IntelliJ IDEA 2021.2.2。新建一個Kotlin工程用來測試。

引入協程

項目用gradle進行管理,我們在Github上找到協程的依賴。

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2")
}

修改項目的gradle配置。把依賴添加進去。

代碼示例

創建一個類然後在main方法中寫協程的相關代碼。

import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

fun main() {
    GlobalScope.launch { // 在後台啟動一個新的協程並繼續
        delay(300)       // 等待300毫秒
        "rustfisher.com".forEach {
            print(it)
            delay(200)   // 每次打印都等待一下
        }
    }
    println("RustFisher")
    Thread.sleep(3000) // 阻塞主線程防止過快退出
}

代碼運行結果

本質上,協程是輕量級的線程。

我們用GlobalScope啟動了一個新的協程,這意味着新協程的生命周期只受整個應用程序的生命周期限制。

可以將 GlobalScope.launch { …… } 替換為 thread { …… },並將 delay(……) 替換為 Thread.sleep(……) 達到同樣目的。注意導入包kotlin.concurrent.thread

協程換成線程

    import java.lang.Thread.sleep
    import kotlin.concurrent.thread

    fun main() {
        thread {
            sleep(300)
            "rustfisher.com".forEach {
                print(it)
                sleep(200)
            }
        }
        println("RustFisher")
        sleep(3000) // 阻塞主線程防止過快退出
    }

如果thread{}中含有delay,編譯器會報錯

Suspend function ‘delay’ should be called only from a coroutine or another suspend function

因為delay是一個特殊的掛起函數,它不會造成線程阻塞,但是會掛起協程,並且只能在協程中使用。

至此,小結一下第一個協程示例

  • gradle引入協程kotlinx-coroutines-core
  • GlobalScope.launch啟動協程
  • 協程中的掛起函數delay(long)可以達到延時的效果,並且它只能在協程中使用

協程所在線程

本質上協程是輕量級的線程。我們觀察一下協程所處的線程信息。

    import kotlinx.coroutines.GlobalScope
    import kotlinx.coroutines.launch
    import java.lang.Thread.sleep

    fun main() {
        println("main線程信息 ${Thread.currentThread().id}")
        for (i in 1..3) { // 多啟動幾次協程
            GlobalScope.launch {
                println("協程啟動#$i 所在線程id: ${Thread.currentThread().id}")
            }
        }
        sleep(2000) // 阻塞主線程防止過快退出
        println("RustFisher 示例結束")
    }

運行log如下

   main線程信息 1
   協程啟動#1 所在線程id: 11
   協程啟動#3 所在線程id: 14
   協程啟動#2 所在線程id: 13
   RustFisher 示例結束

可以看到多次啟動協程,這些協程它們不一定在同一個線程中。也就是說有在同一個線程的可能性。

於是試試瘋狂地啟動協程,把循環次數加大for (i in 1..3000)。觀察log可以看出,有重複的線程id。