在進到 Coroutine 之前,先看看 Kotlin/JVM 開發者過去是怎麼處理多執行緒。這一節整理三組常用工具:Kotlin 的 thread {}、Java 的 Thread、以及 java.util.concurrent 提供的 Executors。它們都還會在後續比較 Coroutine 時當成對照組。

Kotlin 的 thread {}#

Kotlin 標準函式庫提供了 kotlin.concurrent.thread,是建立執行緒(Thread)的最短寫法。

import kotlin.concurrent.thread

fun runInBackground() {
    thread {
        // 這段程式會在新的執行緒上執行
        println("Hello from ${Thread.currentThread().name}")
    }
}

幾個要注意的點:

  • thread {} 預設會立刻啟動,相當於 Java new Thread(...).start()
  • 它不允許從 lambda 直接回傳值,因為 lambda 的型別等同 () -> Unit
  • 想等執行緒完成,可以保留 Thread 變數呼叫 join()
val worker = thread(start = false) {
    // 預先建立但不啟動
}
worker.start()
worker.join()  // 等待完成

Java 的 Thread#

thread {} 底層其實就是 Java 的 Thread,只是包成 Kotlin 慣用語法。直接用 Java API 也很常見。

public class Worker implements Runnable {
    @Override
    public void run() {
        System.out.println("running on " + Thread.currentThread().getName());
    }
}

Thread t = new Thread(new Worker(), "worker-1");
t.start();
t.join();

Thread 的能力有限:建立、啟動、等待結束、設定優先權、設定為 daemon。它不會自動把工作排隊,也不會幫忙重用執行緒。

Executors 與執行緒池#

每來一個工作就 new Thread() 並不划算。java.util.concurrent.Executors 提供了幾種常用的執行緒池:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(() -> doWork());
pool.shutdown();

常見的工廠方法:

工廠方法執行緒配置行為適用場景
newFixedThreadPool(n)固定大小的執行緒池CPU 密集任務
newCachedThreadPool()根據需要建立執行緒,閒置會自動回收短時且大量的任務
newSingleThreadExecutor()固定一條執行緒,任務會依序執行需要依序執行的任務
newScheduledThreadPool(n)支援延遲與排程執行延遲 / 定時排程任務

回傳的 Future<T> 可以拿到結果或取消任務:

Future<Integer> f = pool.submit(() -> 42);
Integer result = f.get();   // 阻塞直到完成
f.cancel(true);             // 嘗試中斷

Executors 解決了「執行緒重用」與「任務排隊」的問題,但仍然停留在「以執行緒為單位」的思考方式。要表達「這個任務由很多步驟組成、每一步都不要阻塞」這類需求,還得加上回呼或 CompletableFuture

同步寫法為什麼不夠用#

把上述工具套到「登入、抓資料、畫畫面」這種步驟接續的情境,會遇到兩個老問題:

  • 阻塞(Blocking):純同步寫法直接 f.get() 會把執行緒卡住,等於把多執行緒的好處抵消掉一部分。
  • 控制權外放:用回呼(Callback)串接時,每一步都把後續邏輯交給上一步呼叫,導致回呼地獄(Callback Hell)與控制權轉移的問題。

後續可以把 ExecutorsCompletableFuture 想成 Coroutine 的前身。它們解決了部分問題,但缺少語言層級的「掛起 / 恢復」機制,所以寫起來仍然偏底層。

小結#

  • Kotlin 的 thread {} 是建立執行緒的便利語法,底層仍是 Java Thread
  • Executors 提供執行緒池與排程能力,避開重複建立執行緒的成本。
  • 這些工具讓多執行緒可寫,但無法本質上解決「非同步程式如何寫得直覺」的問題,這正是 Coroutine 想接手的位置。

原文出處#

  • 原書/iThome:https://ithelp.ithome.com.tw/articles/10259764
  • 書本:Kotlin 小宇宙(iThome 鐵人賽系列書)